diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..b58b603 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/package-lock.json b/package-lock.json index 62879f0..db1510e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,8 +1,221 @@ { "name": "west", "version": "0.0.1", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "west", + "version": "0.0.1", + "dependencies": { + "http-server": "^0.12.3" + } + }, + "node_modules/async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/basic-auth": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.1.0.tgz", + "integrity": "sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/ecstatic": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.2.tgz", + "integrity": "sha512-fLf9l1hnwrHI2xn9mEDT7KIi22UDqA2jaCwyCbSUJh9a1V+LEUSL/JO/6TIz/QyuBURWUHrFL5Kg2TtO1bkkog==", + "dependencies": { + "he": "^1.1.1", + "mime": "^1.6.0", + "minimist": "^1.1.0", + "url-join": "^2.0.5" + }, + "bin": { + "ecstatic": "lib/ecstatic.js" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/follow-redirects": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", + "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-server": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.12.3.tgz", + "integrity": "sha512-be0dKG6pni92bRjq0kvExtj/NrrAd28/8fCXkaI/4piTwQMSDSLMhWyW0NI1V+DBI3aa1HMlQu46/HjVLfmugA==", + "dependencies": { + "basic-auth": "^1.0.3", + "colors": "^1.4.0", + "corser": "^2.0.1", + "ecstatic": "^3.3.2", + "http-proxy": "^1.18.0", + "minimist": "^1.2.5", + "opener": "^1.5.1", + "portfinder": "^1.0.25", + "secure-compare": "3.0.1", + "union": "~0.5.0" + }, + "bin": { + "hs": "bin/http-server", + "http-server": "bin/http-server" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dependencies": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "node_modules/secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=" + }, + "node_modules/union": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", + "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", + "dependencies": { + "qs": "^6.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/url-join": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", + "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=" + } + }, "dependencies": { "async": { "version": "2.6.3", @@ -160,4 +373,4 @@ "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=" } } -} +} \ No newline at end of file diff --git a/src/TaskQueue.js b/src/TaskQueue.js index f3a9c6a..4b985b9 100644 --- a/src/TaskQueue.js +++ b/src/TaskQueue.js @@ -1,55 +1,51 @@ -const TaskQueue = function() { - function TaskQueue() { - this.tasks = []; - this.running = false; - } - - TaskQueue.prototype.push = function(run, dispose, duration) { - if (duration === undefined || duration === null) { - this.tasks.push({runAndContinue: run, dispose}); - } else { - this.tasks.push({ - runAndContinue: (continuation) => { - run(); - setTimeout(() => { - continuation(); - }, duration); - }, - dispose - }); - } - runNextTask(this); - }; - - TaskQueue.prototype.continueWith = function(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; +export default class TaskQueue { + constructor() { + this.tasks = []; + } + + push(run, dispose, duration) { + if (duration === undefined || duration === null) { + this.tasks.push({runAndContinue: run, dispose}); + } else { + this.tasks.push({ + runAndContinue: (continuation) => { + run(); + setTimeout(() => { + continuation(); + }, duration); + }, + dispose, + }); + } + + nextTask(this); + }; + + continueWith(action) { + this.push(action, null, 0); + }; +}; + +function nextTask(taskQueue) { + if (taskQueue.running || taskQueue.tasks.length === 0) return; + taskQueue.running = true; + + const currentTask = taskQueue.tasks.shift(); + + if (currentTask.runAndContinue) { + setTimeout(() => { + currentTask.runAndContinue(() => { + currentTask.dispose && currentTask.dispose(); + taskQueue.running = false; + + setTimeout(() => { + nextTask(taskQueue); + }); + }); + }, 0); + } else { + nextTask(taskQueue); + } +} + +nextTask(new TaskQueue()); \ No newline at end of file diff --git a/src/images/Brewer.jpg b/src/images/Brewer.jpg new file mode 100644 index 0000000..488b919 Binary files /dev/null and b/src/images/Brewer.jpg differ diff --git a/src/images/Dog.jpg b/src/images/Dog.jpg new file mode 100644 index 0000000..c04ad1a Binary files /dev/null and b/src/images/Dog.jpg differ diff --git a/src/images/Duck.jpg b/src/images/Duck.jpg new file mode 100644 index 0000000..eca1b8b Binary files /dev/null and b/src/images/Duck.jpg differ diff --git a/src/images/Gatling.jpg b/src/images/Gatling.jpg new file mode 100644 index 0000000..3a03229 Binary files /dev/null and b/src/images/Gatling.jpg differ diff --git a/src/images/Lad.jpg b/src/images/Lad.jpg new file mode 100644 index 0000000..a5fc49c Binary files /dev/null and b/src/images/Lad.jpg differ diff --git a/src/images/Nemo.jpg b/src/images/Nemo.jpg new file mode 100644 index 0000000..eb2ba8b Binary files /dev/null and b/src/images/Nemo.jpg differ diff --git a/src/images/PseudoDuck.jpg b/src/images/PseudoDuck.jpg new file mode 100644 index 0000000..3167c95 Binary files /dev/null and b/src/images/PseudoDuck.jpg differ diff --git a/src/images/Rogue.jpg b/src/images/Rogue.jpg new file mode 100644 index 0000000..b6d9c50 Binary files /dev/null and b/src/images/Rogue.jpg differ diff --git a/src/images/Trasher.jpg b/src/images/Trasher.jpg new file mode 100644 index 0000000..9ba0a31 Binary files /dev/null and b/src/images/Trasher.jpg differ diff --git a/src/images/bandit.png b/src/images/bandit.png index 80aba7e..9740112 100644 Binary files a/src/images/bandit.png and b/src/images/bandit.png differ diff --git a/src/images/sheriff.png b/src/images/sheriff.png index cb068bd..79b1b62 100644 Binary files a/src/images/sheriff.png and b/src/images/sheriff.png differ diff --git a/src/index.js b/src/index.js index a01f912..2e53c2f 100644 --- a/src/index.js +++ b/src/index.js @@ -3,64 +3,220 @@ import Game from './Game.js'; import TaskQueue from './TaskQueue.js'; import SpeedRate from './SpeedRate.js'; -// Отвечает является ли карта уткой. function isDuck(card) { - return card && card.quacks && card.swims; + return card && card.quacks && card.swims; } -// Отвечает является ли карта собакой. function isDog(card) { - return card instanceof Dog; + return card instanceof Dog; } -// Дает описание существа по схожести с утками и собаками function getCreatureDescription(card) { - if (isDuck(card) && isDog(card)) { - return 'Утка-Собака'; - } - if (isDuck(card)) { - return 'Утка'; - } - if (isDog(card)) { - return 'Собака'; - } - return 'Существо'; + if (isDuck(card) && isDog(card)) { + return 'Утка-Собака'; + } + if (isDuck(card)) { + return 'Утка'; + } + if (isDog(card)) { + return 'Собака'; + } + return 'Существо'; } +class Creature extends Card { + constructor(name, maxPower, image) { + super(name, maxPower, image); + } + setCurrentPower(value) { + this.currentPower = Math.min(this.maxPower, this.currentPower + value); + } -// Основа для утки. -function Duck() { - this.quacks = function () { console.log('quack') }; - this.swims = function () { console.log('float: both;') }; + getDescriptions() { + return [getCreatureDescription(this), ...super.getDescriptions()] + } } +class Duck extends Creature { + constructor(name, power, image) { + super(name ?? "Мирная утка", power ?? 2, image ?? 'Duck.jpg'); + } -// Основа для собаки. -function Dog() { + quacks() { + console.log('quack') + }; + + swims() { + console.log('float: both;') + }; +} + +class Dog extends Creature { + constructor(name, power, image) { + super(name ?? "Пес-бандит", power ?? 3, image ?? 'Dog.jpg'); + } +} + +class Trasher extends Dog { + constructor(name, power, image) { + super(name ?? "Громила", power ?? 5, image ?? 'Trasher.jpg'); + } + + modifyTakenDamage(value, fromCard, gameContext, continuation) { + this.view.signalAbility(() => continuation(value - 1)); + } + + getDescriptions() { + let descriptions = []; + if (Trasher.prototype.hasOwnProperty('modifyTakenDamage')) { + descriptions = ["Получает на 1 меньше урона"]; + } + return descriptions.concat([...super.getDescriptions()]); + } +} + +class Gatling extends Creature { + constructor(name, power, image) { + super(name ?? "Гатлинг", power ?? 6, image ?? 'Gatling.jpg'); + } + + attack(gameContext, continuation) { + let taskQueue = new TaskQueue(); + let oppositePlayer = gameContext.oppositePlayer; + for (let card of oppositePlayer.table) { + taskQueue.push((attack) => this.view.showAttack(attack)); + taskQueue.push((onDone) => this.dealDamageToCreature(2, card, gameContext, onDone)); + } + taskQueue.continueWith(continuation); + game.updateView(); + }; +} + +class Lad extends Dog { + constructor(name, power, image) { + super(name ?? "Браток", power ?? 2, image ?? 'Lad.jpg'); + } + + static getInGameCount() { + return this.inGameCount || 0; + } + + static setInGameCount(value) { + this.inGameCount = value; + } + + static getBonus() { + return this.getInGameCount() * (this.getInGameCount() + 1) / 2; + } + + doAfterComingIntoPlay(gameContext, continuation) { + Lad.setInGameCount(Lad.getInGameCount() + 1); + continuation(); + } + + doBeforeRemoving(continuation) { + Lad.setInGameCount(Lad.getInGameCount() - 1); + continuation(); + }; + + modifyTakenDamage(value, fromCard, gameContext, continuation) { + this.view.signalAbility(() => continuation(value - Lad.getBonus())); + } + + modifyDealedDamageToCreature(value, toCard, 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 { + constructor(name, power, image) { + super(name ?? "Изгой", power ?? 2, image ?? 'Rogue.jpg'); + } + + static stealer = ["modifyDealedDamageToCreature", "modifyDealedDamageToPlayer", "modifyTakenDamage"]; + + modifyTakenDamage(value, fromCard, gameContext, continuation) { + const prototype = Object.getPrototypeOf(fromCard); + for (const property of Rogue.stealer) { + if (prototype.hasOwnProperty(property)) { + this[property] = prototype[property]; + delete prototype[property]; + } + } + + gameContext.updateView(); + super.modifyTakenDamage(value, fromCard, gameContext, continuation); + } + + getDescriptions() { + let descriptions = ["Ворует способности своих врагов"]; + return descriptions.concat([...super.getDescriptions()]); + } +} + +class Brewer extends Duck { + constructor(name, power, image) { + super(name ?? "Пивовар", power ?? 2, image ?? 'Brewer.jpg'); + } + + doBeforeAttack(gameContext, continuation) { + let taskQueue = new TaskQueue(); + let {currentPlayer, oppositePlayer} = gameContext; + let cards = currentPlayer.table.concat(oppositePlayer.table); + for (let card of cards) { + if (isDuck(card)) { + card.maxPower += 1; + card.setCurrentPower(2); + taskQueue.push(onDone => card.view.signalHeal(onDone)); + card.updateView(); + } + } + taskQueue.continueWith(continuation); + } } +class PseudoDuck extends Dog { + constructor(name, power, image) { + super(name ?? "Псевдоутка", power ?? 3, image ?? 'PseudoDuck.jpg'); + } + + quacks() {console.log('quack')}; + swims() {console.log('float: both;')}; +} -// Колода Шерифа, нижнего игрока. -const seriffStartDeck = [ - new Card('Мирный житель', 2), - new Card('Мирный житель', 2), - new Card('Мирный житель', 2), -]; +class Nemo extends Creature { + constructor(name, power, image) { + super(name ?? "Немо", power ?? 4, image?? 'Nemo.jpg'); + } -// Колода Бандита, верхнего игрока. -const banditStartDeck = [ - new Card('Бандит', 3), -]; + doBeforeAttack(gameContext, continuation) { + let taskQueue = new TaskQueue(); + let {oppositePlayer, position} = gameContext; + let oppositeCard = oppositePlayer.table[position]; + if (oppositeCard) { + Object.setPrototypeOf(this, Object.getPrototypeOf(oppositeCard)); + gameContext.updateView(); + } + taskQueue.continueWith(continuation); + } +} -// Создание игры. +const seriffStartDeck = [new Nemo()]; +const banditStartDeck = [new Brewer(), new Brewer()]; const game = new Game(seriffStartDeck, banditStartDeck); -// Глобальный объект, позволяющий управлять скоростью всех анимаций. -SpeedRate.set(1); +SpeedRate.set(2); -// Запуск игры. game.play(false, (winner) => { - alert('Победил ' + winner.name); + alert('Победил ' + winner.name); }); diff --git a/src/styles.css b/src/styles.css index 5715729..26bf9a7 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,9 +1,9 @@ body { - margin: 0px; + margin: 0; } .container { - font-family: Arial; + font-family: Arial, serif; display: grid; grid-template-rows: auto auto auto; grid-row-gap: 10px; @@ -167,8 +167,8 @@ body { } .cardImage { - left: 5px; - right: 5px; + left: 0; + right: 0; position: absolute; height: 100%; text-align: center;