├── .gitignore ├── src ├── img │ └── battlefield-js.jpg ├── js │ ├── metrika.js │ ├── battlefield.options.js │ ├── battlefield.class-field.js │ ├── battlefield.helper.js │ ├── battlefield.class-battlefield.js │ ├── battlefield.class-battle.js │ └── battlefield.class-gameui.js └── css │ ├── battlefield.modal-window.css │ ├── battlefield.player-name.css │ ├── battlefield.logger.css │ ├── battlefield.global.css │ ├── battlefield.fields.css │ └── battlefield.helper.css ├── bower.json ├── test ├── index.html └── spec │ └── class.battlefield.js ├── package.json ├── README.md ├── examples ├── index.html └── development.html ├── gulpfile.js └── dist ├── battlefield.min.js └── battlefield.min.css /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | bower_components 3 | node_modules 4 | npm-debug.log -------------------------------------------------------------------------------- /src/img/battlefield-js.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alx2das/battlefield-js/HEAD/src/img/battlefield-js.jpg -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "battlefield-js", 3 | "description": "This is classic game Battlefield for browsers, implemented on language JavaScript.", 4 | "main": "examples/index.html", 5 | "authors": [ 6 | "Alexandr Builov" 7 | ], 8 | "license": "ISC", 9 | "keywords": [ 10 | "battlefield", 11 | "javascript", 12 | "gulp", 13 | "jasmine" 14 | ], 15 | "homepage": "https://github.com/alx2das/battlefield-js", 16 | "ignore": [ 17 | "**/.*", 18 | "node_modules", 19 | "bower_components", 20 | "test", 21 | "tests" 22 | ], 23 | "dependencies": { 24 | "jasmine-core": "jasmine#^2.5.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/js/metrika.js: -------------------------------------------------------------------------------- 1 | (function (d, w, c) { 2 | (w[c] = w[c] || []).push(function () { 3 | try { 4 | w.yaCounter42568409 = new Ya.Metrika({ 5 | id: 42568409, 6 | clickmap: true, 7 | trackLinks: true, 8 | accurateTrackBounce: true 9 | }); 10 | } catch (e) { 11 | } 12 | }); 13 | 14 | var n = d.getElementsByTagName("script")[0], 15 | s = d.createElement("script"), 16 | f = function () { 17 | n.parentNode.insertBefore(s, n); 18 | }; 19 | s.type = "text/javascript"; 20 | s.async = true; 21 | s.src = "https://mc.yandex.ru/metrika/watch.js"; 22 | 23 | if (w.opera == "[object Opera]") { 24 | d.addEventListener("DOMContentLoaded", f, false); 25 | } else { 26 | f(); 27 | } 28 | })(document, window, "yandex_metrika_callbacks"); -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Battlefield JS 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "battlefield-js", 3 | "version": "1.0.1", 4 | "description": "This is classic game Battlefield for browsers, implemented on language JavaScript.", 5 | "main": "examples/index.html", 6 | "directories": { 7 | "example": "examples", 8 | "test": "test" 9 | }, 10 | "dependencies": {}, 11 | "devDependencies": { 12 | "autoprefixer": "^6.7.2", 13 | "bower": "^1.8.0", 14 | "del": "^2.2.2", 15 | "gulp": "^3.9.1", 16 | "gulp-concat": "^2.6.1", 17 | "gulp-minify-css": "^1.2.4", 18 | "gulp-postcss": "^6.3.0", 19 | "gulp-rename": "^1.2.2", 20 | "gulp-uglify": "^2.0.0", 21 | "run-sequence": "^1.2.2" 22 | }, 23 | "scripts": { 24 | "test": "test/index.html" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "git+https://github.com/alx2das/battlefield-js.git" 29 | }, 30 | "keywords": [ 31 | "battlefield", 32 | "javascript", 33 | "gulp", 34 | "jasmine" 35 | ], 36 | "author": "Alexandr Builov", 37 | "license": "ISC", 38 | "bugs": { 39 | "url": "https://github.com/alx2das/battlefield-js/issues" 40 | }, 41 | "homepage": "https://alx2das.github.io/battlefield-js/examples/" 42 | } 43 | -------------------------------------------------------------------------------- /src/css/battlefield.modal-window.css: -------------------------------------------------------------------------------- 1 | .bf-modal-font { 2 | position: fixed; 3 | z-index: 9; 4 | top: 0; 5 | left: 0; 6 | width: 100%; 7 | height: 100%; 8 | background: rgba(122, 139, 159, 0.8); 9 | } 10 | 11 | .bf-modal-window { 12 | position: fixed; 13 | z-index: 19; 14 | top: 50%; 15 | left: 50%; 16 | width: 600px; 17 | background: #fff; 18 | border-radius: 4px; 19 | } 20 | 21 | .bf-modal-window .content { 22 | padding: 25px 15px 15px 15px; 23 | } 24 | 25 | .bf-modal-window .content span { 26 | float: right; 27 | font-size: 12px; 28 | font-weight: 400; 29 | padding: 2px 0; 30 | color: #7A8BB4; 31 | font-style: italic; 32 | } 33 | 34 | .bf-modal-window .header, .bf-modal-window .cont, .bf-modal-window .footer { 35 | } 36 | 37 | .bf-modal-window .header { 38 | font-weight: 700; 39 | font-size: 16px; 40 | padding-bottom: 16px; 41 | border-bottom: 2px solid rgba(122, 139, 159, .3); 42 | } 43 | 44 | .bf-modal-window .cont { 45 | padding: 30px 0; 46 | } 47 | 48 | .bf-modal-window .footer { 49 | padding-top: 15px; 50 | border-top: 2px solid rgba(122, 139, 159, .3); 51 | } 52 | 53 | .bf-modal-window .header:after, .bf-modal-window .cont:after, .bf-modal-window .footer:after { 54 | content: " "; 55 | clear: both; 56 | display: block; 57 | } -------------------------------------------------------------------------------- /src/css/battlefield.player-name.css: -------------------------------------------------------------------------------- 1 | .bf-player-name { 2 | text-align: center; 3 | width: 950px; 4 | margin: 0 auto 5 | } 6 | 7 | .bf-player-name span { 8 | display: inline-block; 9 | padding: 0 10px; 10 | font-size: 15px; 11 | height: 24px; 12 | line-height: 24px; 13 | } 14 | 15 | .bf-player-name .name { 16 | width: 120px; 17 | background: #7a8b9f; 18 | color: #fff; 19 | margin: 0 10px 0 6px; 20 | padding: 4px 10px; 21 | border-radius: 3px; 22 | height: auto; 23 | line-height: 1.1; 24 | } 25 | 26 | .bf-player-name .name.act { 27 | background: #f0ad4e; 28 | } 29 | 30 | .bf-player-name .total { 31 | min-width: 30px; 32 | text-align: center; 33 | color: #7a8b9f; 34 | font-weight: 700; 35 | } 36 | 37 | .bf-player-name .opt { 38 | float: left 39 | } 40 | 41 | .bf-player-name .left { 42 | float: left; 43 | margin-top: -5px 44 | } 45 | 46 | .bf-player-name .center { 47 | width: 460px; 48 | margin: 0 auto 49 | } 50 | 51 | .bf-player-name .center:after { 52 | content: " "; 53 | clear: both; 54 | display: block; 55 | } 56 | 57 | .bf-player-name .right { 58 | float: right 59 | } 60 | 61 | .bf-player-name .left a { 62 | color: #7a8b9f; 63 | font-weight: 700; 64 | font-size: 26px; 65 | text-decoration: none; 66 | } 67 | 68 | .bf-player-name .right span { 69 | padding: 0 70 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Battlefield 1.0.1 2 | Игра была разработанна шутки ради, практики для, в свободное от основной работы время и помере желания выседания перед компьютером. Алгоритмы игры стараются максимально соответствовать правилам классической игры "Морской бой". Присутствует подобие искуственного интелекта (не обучается), старается сделать минимальное количество выстрелов для победы. 3 | 4 | > Игра может не запуститься в браузерах Internet Explorer, при чем даже в 11 верии, 5 | > все за за `Object.assign()` и многих других мелочей которые не поддерживаются в IE. 6 | 7 | 8 | ## Примеры 9 | [Основной пример можно мосмотреть GitHub Pages](https://alx2das.github.io/battlefield-js/examples/). 10 | 11 | 12 | ## Сборка 13 | Для сборки у вас уже должен быть установлен [Node.js с NPM](https://nodejs.org/). Все необходимые зависимости приписаны в файле `package.json`. 14 | Я использовал [Gulp](http://gulpjs.com/), поэтому у Вас он должен быть установлен глобально. 15 | 16 | ```sh 17 | npm i gulp -g 18 | ``` 19 | 20 | Для запуска используйте команду по умолчанию: 21 | 22 | ```sh 23 | gulp 24 | ``` 25 | 26 | 27 | ## Тестирование 28 | Для тестирования я использовал [Jasmine](https://jasmine.github.io/2.0/introduction.html). Пока что реализованно только браузерное тестирование, пожже добавлю серверное при сборке. 29 | 30 | Для запуска тестов должен быть глобально установлен `bower` и все необходимые зависимости которые описаны в файле `bower.json`: 31 | 32 | ```sh 33 | npm i bower -g 34 | bower i 35 | ``` 36 | 37 | Максимальную информацию по опциям Вы можите увидеть в [исходном коде](https://github.com/alx2das/battlefield-js/tree/master/src). 38 | 39 | 40 | #### [Лицензия ISC](https://ru.wikipedia.org/wiki/%D0%9B%D0%B8%D1%86%D0%B5%D0%BD%D0%B7%D0%B8%D1%8F_ISC) -------------------------------------------------------------------------------- /src/css/battlefield.logger.css: -------------------------------------------------------------------------------- 1 | .bf-logger { 2 | max-width: 950px; 3 | margin: 0 auto; 4 | } 5 | 6 | .bf-logger h2 { 7 | padding-bottom: 15px; 8 | } 9 | 10 | .bf-logger ul { 11 | list-style: none; 12 | max-height: 300px; 13 | overflow-y: auto; 14 | margin-bottom: 25px; 15 | } 16 | 17 | .bf-logger ul li { 18 | display: block; 19 | padding: 7px 0; 20 | border-bottom: 1px solid #d6d6d6; 21 | } 22 | 23 | .bf-logger ul li.no-active { 24 | display: none; 25 | } 26 | 27 | .bf-logger ul li:hover { 28 | background-color: #f6f6f6 29 | } 30 | 31 | .game-log ul li:last-child { 32 | border-bottom: none; 33 | } 34 | 35 | .bf-logger ul li .point, .bf-logger ul li .type, .bf-logger ul li .time { 36 | width: 60px; 37 | display: inline-block; 38 | } 39 | 40 | .bf-logger ul li .point, .bf-logger ul li .time { 41 | font-weight: 700 42 | } 43 | 44 | .bf-logger ul li .point { 45 | } 46 | 47 | .bf-logger ul li .type { 48 | margin: 0 10px; 49 | font-size: 12px; 50 | padding: 2px 0; 51 | text-align: center; 52 | border-radius: 2px; 53 | font-weight: 700; 54 | } 55 | 56 | .bf-logger ul li .type.kil { 57 | background-color: #d9534f; 58 | color: #fff; 59 | } 60 | 61 | .bf-logger ul li .type.war { 62 | background-color: #f0ad4e; 63 | color: #fff; 64 | } 65 | 66 | .bf-logger ul li .type.nul { 67 | } 68 | 69 | .bf-logger ul li .name { 70 | font-size: 14px 71 | } 72 | 73 | .bf-logger ul li .time { 74 | float: right; 75 | } 76 | 77 | .bf-logger .bf-filter { 78 | float: right; 79 | font-weight: 400; 80 | font-size: 13px; 81 | } 82 | 83 | .bf-logger .bf-filter.no-active { 84 | display: none; 85 | } 86 | 87 | .bf-logger .bf-filter span.active { 88 | border-bottom: 1px solid; 89 | } -------------------------------------------------------------------------------- /src/js/battlefield.options.js: -------------------------------------------------------------------------------- 1 | var options = {}, // глобальный объект с опциями игры 2 | // публичные свойства, могут быть изменены через config 3 | _optionsPublic = { 4 | printName: true, // печатать имена игроков 5 | printLog: true, // ведения журнала боя 6 | printHelp: true, // использовать подсказки 7 | 8 | fSize: { // размерность игрового поля 9 | h: 15, v: 15 // h - горизонтально, v - вертикально 10 | }, 11 | fBarrier: [ // список кораблей на игровом поле 12 | [5, 2], [4, 3], [3, 4], [2, 5], [1, 6] // по принципу: ["кол-во палуб", "кол-во штук"] 13 | ], 14 | fMarker: { // обозначение ячеек 15 | h: 'number', v: 'char' // могут быть: char или number 16 | } 17 | }, 18 | // приватные, не доступны для изменения 19 | _optionsPrivate = { 20 | player: { // коды играков, должны начинаться с буквы 21 | kUser: 'FUser', kBrain: 'FBrain' 22 | }, 23 | winner: { 24 | kUser: 0, kBrain: 0 25 | }, 26 | tPoint: { // типы точек игрового поля 27 | DEF: 0, BAR: 1, KIL: 2, NUL: 3 // пусто, корабль, ранил, мимо 28 | } 29 | }; -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Battlefield JS 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 |
15 | Battlefield-js 16 | v1.0.1 17 |
18 |
19 |
27 |
28 |
29 |
30 |
31 | 32 |
33 |
34 | 42 |
43 | 46 | 47 | 48 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /examples/development.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Battlefield JS 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |
19 |
20 | Battlefield-js 21 | v1.0.1 22 |
23 |
24 |
32 |
33 |
34 |
35 |
36 | 37 |
38 |
39 | 47 |
48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/css/battlefield.global.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0 4 | } 5 | 6 | html, body { 7 | min-height: 100%; 8 | height: 100%; 9 | font-family: Arial, sans-serif; 10 | } 11 | 12 | .wrap { 13 | width: 950px; 14 | margin: 0 auto; 15 | } 16 | 17 | .container { 18 | min-height: 100%; 19 | } 20 | 21 | .container .clear-footer { 22 | height: 46px; 23 | } 24 | 25 | .ctn-header:after, .ctn-footer:after { 26 | content: " "; 27 | clear: both; 28 | display: block; 29 | } 30 | 31 | .ctn-header { 32 | padding: 20px 0; 33 | border-bottom: 1px solid #7a8b9f; 34 | } 35 | 36 | .ctn-header .logotype { 37 | float: left; 38 | color: #7a8b9f; 39 | font-weight: 700; 40 | font-size: 26px; 41 | text-decoration: none; 42 | } 43 | 44 | .ctn-header .logotype small { 45 | font-weight: 400; 46 | font-size: 12px; 47 | color: #b5b5b5; 48 | } 49 | 50 | .ctn-header .soc-btn { 51 | float: right; 52 | padding: 3px 0; 53 | } 54 | 55 | .ctn-footer { 56 | padding: 15px 0; 57 | margin-top: -46px; 58 | border-top: 1px solid #7a8b9f; 59 | font-size: 13px; 60 | color: #b5b5b5; 61 | } 62 | 63 | .ctn-footer .copy { 64 | float: left; 65 | } 66 | 67 | .ctn-footer .file { 68 | float: left; 69 | margin-left: 25px; 70 | } 71 | 72 | .ctn-footer .file a { 73 | color: #7a8b9f; 74 | text-decoration: none; 75 | } 76 | 77 | .ctn-footer .file a:hover { 78 | border-bottom: 1px solid #7a8b9f 79 | } 80 | 81 | .bf-player-name, .bf-fields, .bf-logger, .bf-modal-window, .btn { 82 | font-family: Arial, sans-serif; 83 | color: #7a8b9f; 84 | font-size: 13px; 85 | } 86 | 87 | .bf-player-name, .bf-fields, .bf-logger { 88 | padding: 25px 0; 89 | } 90 | 91 | .bf-player-name:after, .bf-fields:after, .bf-logger:after { 92 | content: " "; 93 | clear: both; 94 | display: block; 95 | } 96 | 97 | .btn { 98 | color: #fff; 99 | float: left; 100 | margin-right: 10px; 101 | outline: 0; 102 | margin-bottom: 0; 103 | font-weight: 400; 104 | text-align: center; 105 | vertical-align: middle; 106 | cursor: pointer; 107 | border: 1px solid transparent; 108 | white-space: nowrap; 109 | padding: 7px 14px; 110 | font-size: 13px; 111 | line-height: 1.2; 112 | border-radius: 3px; 113 | -webkit-user-select: none; 114 | -moz-user-select: none; 115 | -ms-user-select: none; 116 | user-select: none; 117 | } 118 | 119 | .btn { 120 | background-color: #5cb85c; 121 | color: #fff; 122 | } 123 | 124 | .btn:active { 125 | background-color: #449d44; 126 | } 127 | 128 | .btn:hover { 129 | background-color: #449d44; 130 | } 131 | 132 | .btn.btn-warning { 133 | background-color: #f0ad4e; 134 | } 135 | 136 | .btn.btn-warning:active { 137 | background-color: #ec971f; 138 | } 139 | 140 | .btn.btn-warning:hover { 141 | background-color: #ec971f; 142 | } 143 | 144 | .btn.btn-danger { 145 | background-color: #d9534f; 146 | float: right; 147 | margin-right: 0 148 | } 149 | 150 | .btn.btn-danger:active { 151 | background-color: #c9302c; 152 | background-image: none; 153 | } 154 | 155 | .btn.btn-danger:hover { 156 | background-color: #c9302c; 157 | } 158 | 159 | span.js { 160 | margin-left: 15px; 161 | cursor: pointer; 162 | color: #7a8b9f; 163 | border-bottom: 1px dashed #7a8b9f; 164 | } 165 | 166 | span.js:hover { 167 | border-bottom: none 168 | } -------------------------------------------------------------------------------- /test/spec/class.battlefield.js: -------------------------------------------------------------------------------- 1 | describe('Публичный класс Battlefield()', function() { 2 | var BF; 3 | 4 | beforeEach(function () { 5 | options = {}; 6 | _instances = false; 7 | }); 8 | afterEach(function () { 9 | BF = undefined; 10 | }); 11 | 12 | it("Применение публичных опций", function() { 13 | BF = new Battlefield('htmlSelector', { 14 | fSize: { h: 22, v: 23 }, 15 | fBarrier: [ 16 | [6, 1], [5, 2], [4, 3], [3, 3], [2, 4], [1, 6] 17 | ] 18 | }); 19 | 20 | expect(options.fSize.h).toEqual(22); 21 | expect(options.fSize.v).toEqual(23); 22 | expect(options.fBarrier.length).toEqual(6); 23 | expect(options.fBarrier[0]).toEqual([6, 1]); 24 | }); 25 | 26 | it("Игнорирование изменения приватных опций", function() { 27 | BF = new Battlefield('htmlSelector', { 28 | player: { 29 | kUser: 'BlaBlaUser', kBrain: 'BlaBlaBrain' 30 | }, 31 | tPoint: { 32 | DEF: '12', BAR: '13', KIL: '14', NUL: '15' 33 | } 34 | }); 35 | 36 | expect(options.player.kUser).not.toBe('BlaBlaUser'); 37 | expect(options.player.kBrain).not.toBe('BlaBlaBrain'); 38 | 39 | expect(options.tPoint.DEF).not.toBe('12'); 40 | expect(options.tPoint.DEF).not.toBe('13'); 41 | expect(options.tPoint.DEF).not.toBe('14'); 42 | expect(options.tPoint.DEF).not.toBe('15'); 43 | }); 44 | 45 | it('Установка уровня сложности', function () { 46 | BF = new Battlefield('htmlSelector'); 47 | 48 | expect(options.fSize.h).not.toBe(10); 49 | expect(options.fSize.v).not.toBe(10); 50 | expect(options.fBarrier.length).not.toBe(4); 51 | 52 | BF.setLevel('easy'); 53 | 54 | expect(options.fSize.h).toBe(10); 55 | expect(options.fSize.v).toBe(10); 56 | expect(options.fBarrier.length).toBe(4); 57 | 58 | BF.setLevel('middle'); 59 | 60 | expect(options.fSize.h).toBe(15); 61 | expect(options.fSize.v).toBe(15); 62 | expect(options.fBarrier.length).toBe(5); 63 | 64 | BF.setLevel('hard'); 65 | 66 | expect(options.fSize.h).toBe(15); 67 | expect(options.fSize.v).toBe(20); 68 | expect(options.fBarrier.length).toBe(6); 69 | }); 70 | }); 71 | 72 | describe('Приватный класс Field()', function () { 73 | var BF; 74 | var F; 75 | 76 | beforeEach(function () { 77 | options = {}; 78 | _instances = false; 79 | 80 | BF = new Battlefield('htmlSelector'); 81 | }); 82 | afterEach(function () { 83 | BF = undefined; 84 | }); 85 | 86 | it('Создание игровых полей', function () { 87 | BF.setLevel('easy'); 88 | F = new Field(); 89 | expect(F.fields[options.player.kUser].length).toBe(10); 90 | expect(F.fields[options.player.kUser][0].length).toBe(10); 91 | 92 | delete F; 93 | 94 | BF.setLevel('middle'); 95 | F = new Field(); 96 | expect(F.fields[options.player.kUser].length).toBe(15); 97 | expect(F.fields[options.player.kUser][0].length).toBe(15); 98 | 99 | delete F; 100 | 101 | BF.setLevel('hard'); 102 | F = new Field(); 103 | expect(F.fields[options.player.kUser].length).toBe(20); 104 | expect(F.fields[options.player.kUser][0].length).toBe(15); 105 | }) 106 | }); -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var pkg = require('./package.json'), 4 | gulp = require('gulp'), 5 | autoprefixer = require('autoprefixer'), 6 | postcss = require('gulp-postcss'), 7 | concat = require('gulp-concat'), 8 | uglify = require('gulp-uglify'), 9 | minifyCSS = require('gulp-minify-css'), 10 | rename = require("gulp-rename"), 11 | del = require('del'), 12 | runSequence = require('run-sequence'); 13 | 14 | // очистка директории 15 | gulp.task('clean', function () { 16 | return del('dist'); 17 | }); 18 | 19 | // обьединение файлов 20 | gulp.task('concat', function (callback) { 21 | runSequence( 22 | ['concat:js', 'concat:css'], 23 | callback 24 | ); 25 | }); 26 | 27 | // обьединение JS файлов 28 | gulp.task('concat:js', function () { 29 | return gulp 30 | .src([ 31 | 'src/js/battlefield.options.js', 32 | 'src/js/battlefield.class-battlefield.js', 33 | 'src/js/battlefield.class-field.js', 34 | 'src/js/battlefield.class-battle.js', 35 | 'src/js/battlefield.class-gameui.js', 36 | 'src/js/battlefield.helper.js' 37 | ]) 38 | .on('data', function (file) { 39 | file.contents = new Buffer(file.contents.toString() + '\n'); 40 | }) 41 | .pipe(concat('battlefield.js')) 42 | .on('data', function (file) { 43 | file.contents = new Buffer( 44 | description() + 45 | '(function(window){' + 46 | ' \n' + 47 | ' "use strict";' + 48 | ' \n\n' + 49 | file.contents.toString() + 50 | ' \n' + 51 | ' window.Battlefield = Battlefield;' + 52 | ' \n' + 53 | '})(window);' 54 | ); 55 | }) 56 | .pipe(gulp.dest('dist')); 57 | }); 58 | 59 | // обьединение CSS файлов 60 | gulp.task('concat:css', function () { 61 | return gulp 62 | .src('src/css/*.css') 63 | .pipe(postcss([autoprefixer()])) 64 | .pipe(concat('battlefield.css')) 65 | .on('data', function (file) { 66 | file.contents = new Buffer( 67 | description() + 68 | file.contents.toString() 69 | ) 70 | }) 71 | .pipe(gulp.dest('dist')); 72 | }); 73 | 74 | // минификация файлов 75 | gulp.task('min', function (callback) { 76 | runSequence( 77 | ['min:js', 'min:css'], 78 | callback 79 | ); 80 | }); 81 | 82 | // добавляем метрику 83 | gulp.task('metrika', function () { 84 | return gulp 85 | .src([ 86 | 'dist/battlefield.js', 87 | 'src/js/metrika.js' 88 | ]) 89 | .on('data', function (file) { 90 | file.contents = new Buffer(file.contents.toString() + '\n'); 91 | }) 92 | .pipe(concat('battlefield.js')) 93 | .pipe(gulp.dest('dist')); 94 | }); 95 | 96 | // минификация JS файлов 97 | gulp.task('min:js', function () { 98 | return gulp 99 | .src(['dist/battlefield.js']) 100 | .pipe(rename('battlefield.min.js')) 101 | .pipe(uglify()) 102 | .on('data', function (file) { 103 | file.contents = new Buffer( 104 | description() + 105 | file.contents.toString() 106 | ); 107 | }) 108 | .pipe(gulp.dest('dist')); 109 | }); 110 | 111 | // минификация CSS файлов 112 | gulp.task('min:css', function () { 113 | return gulp 114 | .src(['dist/battlefield.css']) 115 | .pipe(rename('battlefield.min.css')) 116 | .pipe(minifyCSS()) 117 | .on('data', function (file) { 118 | file.contents = new Buffer( 119 | description() + 120 | file.contents.toString() 121 | ) 122 | }) 123 | .pipe(gulp.dest('dist')); 124 | }); 125 | 126 | // главная задача 127 | gulp.task('default', function (callback) { 128 | runSequence( 129 | 'clean', 130 | 'concat', 131 | 'metrika', 132 | 'min', 133 | callback 134 | ); 135 | }); 136 | 137 | // описание для файлов 138 | function description() { 139 | return '' + 140 | '/**\n' + 141 | ' * ' + pkg.name + ' v' + pkg.version + '\n' + 142 | ' * ' + pkg.description + '\n' + 143 | ' * \n' + 144 | ' * repository: ' + pkg.repository.url + '\n' + 145 | ' * bugs: ' + pkg.repository.url + '\n' + 146 | ' * homepage: ' + pkg.homepage + '\n' + 147 | ' * \n' + 148 | ' * Copyright 2017, ' + pkg.author + '\n' + 149 | ' * Date: ' + new Date().toDateString() + '\n' + 150 | ' */\n'; 151 | } -------------------------------------------------------------------------------- /src/js/battlefield.class-field.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Класс для создания игрового поля 3 | * 4 | * Требуется: 5 | * options = {} - глобальный объект с опциями игры 6 | * h = {} - глобальный обьект функций помошников 7 | * 8 | * @constructor 9 | */ 10 | function Field() { 11 | if (options.fSize.h < 10 || options.fSize.h > 25 || options.fSize.v < 10 || options.fSize.v > 25) 12 | throw new RangeError(h.getMessage('err_size_field')); 13 | 14 | if (!(options.fBarrier instanceof Array) || options.fBarrier.length < 1) 15 | throw new RangeError(h.getMessage('err_barrier')); 16 | 17 | this.fields = []; 18 | this.fName = [options.player.kUser, options.player.kBrain]; 19 | 20 | for (var player in options.player) { // создание игровых полей 21 | var fKey = options.player[player]; 22 | 23 | var field = new Array(options.fSize.v); 24 | for (var i = 0; i < options.fSize.v; i++) { 25 | field[i] = new Array(options.fSize.h); 26 | 27 | for (var j = 0; j < options.fSize.h; j++) 28 | field[i][j] = options.tPoint.DEF; 29 | } 30 | 31 | this.fields[fKey] = field; 32 | } 33 | } 34 | 35 | /** 36 | * Устанавливает корабли на игровое поле 37 | * 38 | * @returns {Array} 39 | */ 40 | Field.prototype.setBarrier = function () { 41 | var self = this, 42 | maxIteration = this.fName.length * 100; 43 | 44 | this.fName.forEach(function (fKey) { // на каждое игровое поле ставим по отдельности 45 | var field = self.fields[fKey]; 46 | 47 | options.fBarrier.forEach(function (ship) { // перебираем типы кораблей 48 | var cell = ship[0], // кол-во палуб 49 | ctn = ship[1]; // кол-во штук 50 | 51 | for (var c = 0; c < ctn; c++) // устанавливаем по одному 52 | field = shipInField(cell, field); 53 | }); 54 | 55 | self.fields[fKey] = field; 56 | }); 57 | 58 | return this.fields; // игровые поля с кораблями 59 | 60 | 61 | // private method.................... 62 | // .................................. 63 | 64 | // установит корабль на игровое поле 65 | function shipInField(cell, field) { 66 | if (maxIteration > 0) maxIteration--; // от зацикливания рекурсии 67 | else throw new RangeError(h.getMessage('err_max_iteration')); 68 | 69 | var sPoint = [], // массив точек для размещения корабля 70 | x = h.rand(0, options.fSize.v - 1), // случайная координата начала установки 71 | y = h.rand(0, options.fSize.h - 1); 72 | 73 | var posHorizontal = h.rand(1, 2) % 2; // случайное направление, горизонтально/вертикально 74 | 75 | for (var i = 0; i < cell; i++) { // перебор палуб 76 | var pX = x, pY = y; 77 | 78 | if (posHorizontal) pX = x + i; // направление корабля 79 | else pY = y + i; 80 | 81 | if (checkPoint(pX, pY, field)) // если точка свободна, сохраняем ее 82 | sPoint.push([pX, pY]); 83 | else return shipInField(cell, field); // перезапуск 84 | } 85 | 86 | // сюда дайдет если только все точки доступны для размещения 87 | sPoint.forEach(function (point) { // точки которые можно нанести на карту 88 | var x = point[0], 89 | y = point[1]; 90 | 91 | field[x][y] = options.tPoint.BAR; 92 | }); 93 | 94 | return field; // вернем карту с размещенным кораблем 95 | } 96 | 97 | // проверяет выбранную точку + ее окружение 98 | function checkPoint(x, y, field) { 99 | var cP = [[-1, 0], [-1, -1], [0, -1], [1, -1], [1, 0], [1, 1], [0, 1], [-1, 1]], 100 | pX = 0, pY = 0; 101 | var sX = options.fSize.v - 1, 102 | sY = options.fSize.h - 1; 103 | 104 | if (x < 0 || x > sX || y < 0 || y > sY) // исходная точка в рамках игрового поля 105 | return false; 106 | 107 | if (field[x][y] !== options.tPoint.DEF) // точка свободна 108 | return false; 109 | 110 | for (var i = 0; i < cP.length; i++) { // перебор соседних точек 111 | pX = x + cP[i][0]; 112 | pY = y + cP[i][1]; 113 | 114 | if (pX >= 0 && pX <= sX && pY >= 0 && pY <= sY) { // сеседние точки могут быть за гранью поля 115 | if (field[pX][pY] != options.tPoint.DEF) // но не могут быть заняты другим кораблем 116 | return false; 117 | } 118 | } 119 | 120 | return true; // если все условия проверки пройдены, можно ставить 121 | } 122 | }; -------------------------------------------------------------------------------- /src/css/battlefield.fields.css: -------------------------------------------------------------------------------- 1 | .bf-fields { 2 | background-color: #7a8b9f; 3 | } 4 | 5 | .bf-fields:after { 6 | content: " "; 7 | display: block; 8 | clear: both 9 | } 10 | 11 | .bf-fields .board { 12 | width: 50% 13 | } 14 | 15 | .bf-fields .board.lf { 16 | float: left 17 | } 18 | 19 | .bf-fields .board.rg { 20 | float: right 21 | } 22 | 23 | .bf-fields table.field { 24 | border-collapse: collapse; 25 | margin: 25px; 26 | } 27 | 28 | .bf-fields table.field td, .bf-fields table.field td .box { 29 | border: 2px solid #E5EAFF; 30 | } 31 | 32 | .bf-fields table.field td .box, .bf-fields table.field td .ll, .bf-fields table.field td .mm { 33 | width: 28px; 34 | height: 28px; 35 | line-height: 28px; 36 | text-align: center; 37 | } 38 | 39 | .bf-fields table.field td .ll, .bf-fields table.field td .mm { 40 | position: absolute; 41 | z-index: 1; 42 | font-family: Georgia; 43 | color: #afafaf; 44 | font-weight: 700; 45 | font-size: 12px 46 | } 47 | 48 | .bf-fields table.field td .ll { 49 | left: -28px 50 | } 51 | 52 | .bf-fields table.field td .mm { 53 | top: -28px 54 | } 55 | 56 | .bf-fields table.field td { 57 | position: relative; 58 | } 59 | 60 | .bf-fields table.field td .box { 61 | float: left; 62 | margin: -2px 63 | } 64 | 65 | .bf-fields table.field td .box.null, .bf-fields table.field td .box.auto-null, .bf-fields table.field td .box.kill, .bf-fields .list-let .box.kill { 66 | background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAAcCAYAAAA0u3w+AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyFpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NDkxMSwgMjAxMy8xMC8yOS0xMTo0NzoxNiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpDRjMwN0VCOEM4NDcxMUU1OTM5M0RBNkE1MDVDQUVEMSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpDRjMwN0VCOUM4NDcxMUU1OTM5M0RBNkE1MDVDQUVEMSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkNGMUMzMzI1Qzg0NzExRTU5MzkzREE2QTUwNUNBRUQxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkNGMUMzMzI2Qzg0NzExRTU5MzkzREE2QTUwNUNBRUQxIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+aamjFgAAAmdJREFUeNrs2E1IFGEcx/FdldlVWosiyg7WKQo6LNFFykgoL4GKEER16OUQ5kW6RZ3yUKcugVJLJEKIHSSiRKUoO0UvaChSInSIXpBa1y13Y3fW6fvIXxi2nX1zwJ1lHviwLzPzzPyet9lZr2EYnnIuFZ4yL25AN2Cxxeu10oKNWbZn5qAe/Ixh1ObYr8apQ3QKAQkZsNhnK1qdPAcHcVBCbkjrMfU6hJdODjgAdYM+JCG3oBeVsu0bvudTUVUhZz3cdHT17THsxhtMI/7qxTO7wql6w3iNBjRiTi1LuI0WNOVbWVWBJ1cteAPNSOEi7qMPCwXWFcRxLEqYD0hiGRPYbtp3k7x2YCbf4VlMwHbVkSdPtCd9Pl8wkUhMj449Px+ORL7y/cMC65qUBeW0zLk6CabC3sU1i+N6TQvNDmmUGbvmoFrVlgm3XzWOpmlBf031XzlJMUWNgn7sQRfqcRnXoVkccwV/cE8a5aOdi8y4ajld12dXri6VmossRDbLpF9LSUqv7ZUezFZUr92UuTgiQ9q2gEu482R41PPu/cT4o8dPPbF4PMR3v2xYXNScDMk8z1W6ZXXdafcc/IEHi9HoLHS1euKLrHJrLZNy4YN5/BA4g21yr/wpDZ+5qOfBdSE3OpMLWDLUJeXW/9/xFucphYC1GEgLEEU4Q7C3pvddTgh4AJ8whZD04j5UoE+CxHAVOjR04jeSOFLKAf1oRCDDcFV6JOA5+dxh2rYLI5hHfbaA3nX7TybLM5yUW/DjklUNOItTK08WhhEr9UUmXasMyVz71aGt9HrQ/U/GDegGdAOWQ/knwAALfSfZ0tNOqgAAAABJRU5ErkJggg==) 0 0 no-repeat; 67 | } 68 | 69 | .bf-fields table.field td .box.let { 70 | background-color: #E5EAFF 71 | } 72 | 73 | .bf-fields table.field td .box.null { 74 | opacity: 1 !important; 75 | } 76 | 77 | .bf-fields table.field td .box.auto-null { 78 | opacity: .5; 79 | } 80 | 81 | .bf-fields table.field td .box.kill { 82 | background-position: -28px 0; 83 | background-color: #a1b9d6; 84 | } 85 | 86 | .bf-fields table.field td .box.death { 87 | background-color: #deabab 88 | } 89 | 90 | .bf-fields .shot { 91 | position: absolute; 92 | z-index: 1; 93 | width: 28px; 94 | height: 28px; 95 | margin: -2px; 96 | background-color: rgba(255, 249, 0, 0.7); 97 | border: 2px solid #fff900; 98 | } 99 | 100 | .bf-fields .list-let { 101 | margin: 25px 15px 0; 102 | float: left; 103 | } 104 | 105 | .bf-fields .list-let .let { 106 | margin-bottom: 10px 107 | } 108 | 109 | .bf-fields .list-let .let:after { 110 | content: " "; 111 | display: block; 112 | clear: both 113 | } 114 | 115 | .bf-fields .list-let span { 116 | width: 26px; 117 | height: 26px; 118 | line-height: 26px; 119 | margin: 0 10px; 120 | text-align: center; 121 | color: #afafaf; 122 | font-weight: 700; 123 | display: inline-block; 124 | background: 0 0; 125 | border: none; 126 | } 127 | 128 | .bf-fields .list-let span .shot { 129 | width: 24px; 130 | height: 24px; 131 | margin: -27px 0 0 0; 132 | } 133 | 134 | .bf-fields .list-let .box { 135 | float: left; 136 | width: 24px; 137 | height: 24px; 138 | margin-left: -2px; 139 | background-color: rgba(165, 188, 208, .5) !important; 140 | border: 2px solid #E5EAFF; 141 | } 142 | 143 | .bf-fields .list-let .box.kill { 144 | background-position: -28px 0; 145 | } 146 | 147 | .bf-fields .board.rg table.field td .box { 148 | cursor: pointer; 149 | } 150 | 151 | .bf-fields .board.rg table.field td .box:hover { 152 | box-shadow: inset 0 0 5px 2px #ff8d8d; 153 | } 154 | 155 | .bf-fields .board.rg table.field td .box.kill:hover, 156 | .bf-fields .board.rg table.field td .box.let:hover, 157 | .bf-fields .board.rg table.field td .box.null:hover, 158 | .bf-fields .board.rg table.field td .box.auto-null:hover { 159 | box-shadow: none !important; 160 | cursor: default !important; 161 | } 162 | 163 | .bf-fields .board table.field.timeout { 164 | opacity: 0.7; 165 | -webkit-user-select: none; 166 | -moz-user-select: none; 167 | -ms-user-select: none; 168 | user-select: none; 169 | } 170 | 171 | .bf-fields .board table.field.timeout .box { 172 | cursor: wait !important; 173 | } 174 | 175 | .bf-fields .board.lf table.field, 176 | .bf-fields .board.lf .list-let, 177 | .bf-fields .board.lf .list-let .box, 178 | .bf-fields .board.rg .list-let span { 179 | float: right 180 | } 181 | 182 | .bf-fields .board.rg table.field, 183 | .bf-fields .board.rg .list-let, 184 | .bf-fields .board.rg .list-let .box, 185 | .bf-fields .board.lf .list-let span { 186 | float: left 187 | } -------------------------------------------------------------------------------- /src/js/battlefield.helper.js: -------------------------------------------------------------------------------- 1 | // глобальный класс модуля 2 | var h = { 3 | /** 4 | * имена играков по умолчанию 5 | */ 6 | playerName: { 7 | def: { 8 | kUser: 'Игрок', kBrain: 'Компьютер' 9 | } 10 | }, 11 | 12 | /** 13 | * Вернет случайное число в интервале 14 | * 15 | * @param min - по умолчанию: 0 16 | * @param max - по умолчанию: 100 17 | * @returns {Number} 18 | */ 19 | rand: function (min, max) { 20 | min = min || 0; 21 | max = max || 100; 22 | 23 | return parseInt(Math.random() * (max - min + 1) + min); 24 | }, 25 | 26 | /** 27 | * Вернет буквку англ.алфавита по номеру. 28 | * Если номер больше кол-ва букв, к результату будет прибавлена цифра 29 | * 30 | * @param key 31 | * @param operand 32 | * @returns {*} 33 | */ 34 | getLetter: function (key, operand) { 35 | var operand = operand || '', 36 | alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 37 | if (key > alphabet.length) 38 | return this.getLetter(key - alphabet.length, (operand == '' ? 1 : operand + 1)); 39 | 40 | return alphabet[key] + operand; 41 | }, 42 | 43 | /** 44 | * Перемешает массив случайным образом 45 | * 46 | * @param arr - массив 47 | * @returns {*} 48 | */ 49 | shuffle: function (arr) { 50 | var inx, buffer; 51 | for (var i = 0; i < arr.length - 1; i++) { 52 | inx = this.rand(0, arr.length - 1); 53 | buffer = arr[inx]; 54 | 55 | arr[inx] = arr[arr.length - 1]; 56 | arr[arr.length - 1] = buffer; 57 | } 58 | return arr; 59 | }, 60 | 61 | /** 62 | * Интернализация 63 | * 64 | * @param type 65 | * @returns {*} 66 | */ 67 | getMessage: function (type) { 68 | var mess = { 69 | start_game: 'Начать игру', 70 | new_game: 'Новая игра', 71 | game_over: 'Игра закончена', 72 | you_lose: 'Ты проиграл', 73 | you_winner: 'Ты выиграл', 74 | options: 'Настройки', 75 | update_options: 'Изменить настройки', 76 | close: 'Закрыть', 77 | update_page: 'Обновить страницу', 78 | set_default_params: 'Настройки по умолчанию', 79 | save: 'Сохранить', 80 | filter: 'Фильтр', 81 | full: 'Все', 82 | 83 | log_kill: 'ранил', 84 | log_death: 'убил', 85 | log_past: 'мимо', 86 | 87 | // error 88 | err_invalid_selector: 'Указанный HTML блок на странице не найден', 89 | err_max_iteration: 'Не получилось установить корабли на игровое поле, измените настройки', 90 | err_invalid_player: 'Неизвестный игрок', 91 | err_invalid_field: 'Некорректное игровое поле', 92 | err_player_name: 'Переданны не корректные ключи играков', 93 | err_size_field: 'Игровое поле не должно быть меньше 10х10 или больше 25х25, измените настройки', 94 | err_barrier: 'Не корректный список кораблей', 95 | err_invalid_level: 'Указан несуществующий уровень сложности', 96 | 97 | // info 98 | info_log_title: 'Состояние игрового поля', 99 | info_title_error: 'Критическая ошибка', 100 | info_title_range_error: 'Некорректные настройки', 101 | info_winner: 'Замечательная победа,
попробуешь еще?', 102 | info_loser: 'В следующий раз повезет больше,
попробуешь еще?' 103 | }; 104 | 105 | return typeof mess[type] == 'string' && mess[type].length > 0 ? mess[type] : type; 106 | }, 107 | 108 | /** 109 | * Вернет строковое имя игрока 110 | * 111 | * @param fKey - строковый ключ игрока 112 | * @returns {*} 113 | */ 114 | getPlayerName: function (fKey) { 115 | var key = fKey == options.player.kUser ? 'kUser' : 'kBrain'; 116 | return this.playerName.def[key]; 117 | }, 118 | 119 | /** 120 | * Ловит сообщение об ошибке 121 | * 122 | * @param err 123 | */ 124 | showExceptions: function (err) { 125 | var title = '', 126 | content = '', 127 | button = []; 128 | 129 | switch (err.name) { 130 | case ('Error'): 131 | title = h.getMessage('info_title_error'); 132 | content = err.message; 133 | button = [{ 134 | elValue: h.getMessage('set_default_params'), 135 | onClick: function (modal) { 136 | modal.close(); 137 | var b = new Battlefield(options.htmlSelector, options); 138 | b.setLevel('middle').run(); 139 | } 140 | }]; 141 | break; 142 | case ('RangeError'): 143 | title = h.getMessage('info_title_range_error'); 144 | content = err.message; 145 | button = [{ 146 | elValue: h.getMessage('update_options'), 147 | onClick: function (modal) { 148 | modal.close(); 149 | var b = new Battlefield(options.htmlSelector, options); 150 | b.updateConfig(); 151 | } 152 | },{ 153 | elValue: h.getMessage('set_default_params'), 154 | elClass: 'btn-warning', 155 | onClick: function (modal) { 156 | modal.close(); 157 | var b = new Battlefield(options.htmlSelector, options); 158 | b.setLevel('middle').run(); 159 | } 160 | }]; 161 | break; 162 | default: 163 | title = err.name; 164 | content = err.message; 165 | } 166 | 167 | this.modalWindow(title, content, button); 168 | }, 169 | 170 | /** 171 | * Выводит модальное окно, автоматически установит по середине страницы 172 | * 173 | * ~~~~ 174 | * h.modalWindow( 175 | * 'title modal window', 176 | * 'Content modal', 177 | * [{ 178 | * elClass: 'btn-success', // css класс будет добавлен к кнопке 179 | * elValue: 'Button close', // значение кнопки 180 | * onClick: function (modal, event) {} // событие клика по кнопке 181 | * }] 182 | * ) 183 | * ~~~~ 184 | * 185 | * @param title - заголовок 186 | * @param contentHTML - html содержимое 187 | * @param button - массив обьектов кнопок модального окна 188 | */ 189 | modalWindow: function (title, contentHTML, button) { 190 | var modal = { 191 | font: false, 192 | window: false, 193 | 194 | construct: function () { 195 | this.createHTML().autoPosition(); 196 | 197 | if (!(button instanceof Array) || button.length == 0) { 198 | this.window.querySelector('.footer').style.display = 'none'; 199 | return false; 200 | } 201 | 202 | var footer = this.window.querySelector('.footer'), 203 | on = []; 204 | 205 | for (var i = 0; i < button.length; i++) { 206 | var btn = button[i], 207 | dopClass = typeof button[i].elClass == 'string' ? ' ' + btn.elClass : ''; 208 | 209 | on[i] = document.createElement('button'); 210 | on[i].setAttribute('class', 'btn' + dopClass); 211 | on[i].setAttribute('id', 'btn_' + i); 212 | on[i].innerHTML = btn.elValue; 213 | on[i].addEventListener('click', eventListener(i, button), false); 214 | 215 | footer.appendChild(on[i]); 216 | } 217 | }, 218 | createHTML: function () { 219 | var _font = document.querySelector('.bf-modal-font'), 220 | _window = document.querySelector('.bf-modal-window'); 221 | if (_font != null) document.body.removeChild(_font); 222 | if (_window != null) document.body.removeChild(_window); 223 | 224 | 225 | this.font = document.createElement('div'); 226 | this.font.setAttribute('class', 'bf-modal-font'); 227 | 228 | this.window = document.createElement('div'); 229 | this.window.setAttribute('class', 'bf-modal-window'); 230 | this.window.innerHTML = 231 | '
' + 232 | '
' + title + '
' + 233 | '
' + contentHTML + '
' + 234 | ' ' + 235 | '
'; 236 | 237 | document.body.appendChild(this.font); 238 | document.body.appendChild(this.window); 239 | 240 | return this; 241 | }, 242 | autoPosition: function () { 243 | var wid2 = this.window.clientWidth / 2, 244 | hei2 = this.window.clientHeight / 2; 245 | 246 | this.window.style.marginTop = hei2 * (-1) + 'px'; 247 | this.window.style.marginLeft = wid2 * (-1) + 'px'; 248 | }, 249 | close: function () { 250 | document.body.removeChild(this.font); 251 | document.body.removeChild(this.window); 252 | } 253 | }; 254 | 255 | modal.construct(); 256 | 257 | 258 | // private method.................... 259 | // .................................. 260 | 261 | // вешает событие на кнопку в модальном окне 262 | function eventListener(i, obj) { 263 | return function (event) { 264 | if (typeof obj[i].onClick == 'function') 265 | obj[i].onClick(modal, event); 266 | }; 267 | } 268 | }, 269 | 270 | /** 271 | * Запускает анимацию плавного исчезновения со страницы 272 | * 273 | * @param element - document элемент 274 | * @param time - кол-во сек.через которое элемент исчезнет со страницы 275 | */ 276 | animateOpacity: function (element, time) { 277 | element.style.opacity = 1; 278 | var t = setInterval(function () { 279 | element.style.opacity = element.style.opacity - (100 / (time / 0.1)); 280 | if (element.style.opacity <= 0) { 281 | clearInterval(t); 282 | try { 283 | element.parentNode.removeChild(element); 284 | } catch (e) { } 285 | } 286 | }, 1); 287 | } 288 | }; -------------------------------------------------------------------------------- /src/js/battlefield.class-battlefield.js: -------------------------------------------------------------------------------- 1 | var _instances = false, // Singleton 2 | _globalLevel = 'middle'; // уровень сложности по умолчанию 3 | 4 | /** 5 | * Инициализирует игру, устанавливает параметры по умолчанию 6 | * 7 | * Требуется: 8 | * options = {} - глобальный объект с опциями игры 9 | * h = {} - глобальный обьект функций помошников 10 | * 11 | * @param htmlSelector - строковый селектор элемента на странице 12 | * @param config - объект настроек по умолчанию, доступные параметры: _optionsPublic 13 | * @returns {Battlefield} 14 | * @constructor 15 | */ 16 | function Battlefield(htmlSelector, config) { 17 | // Singleton 18 | if (_instances instanceof Battlefield) 19 | return _instances; 20 | _instances = this; 21 | 22 | // объединяем установленные опции игры с доступными 23 | if (typeof config == 'object') { 24 | for (var key in _optionsPublic) 25 | options[key] = config.hasOwnProperty(key) ? config[key] : _optionsPublic[key]; 26 | options = Object.assign(options, _optionsPrivate); 27 | } 28 | else options = Object.assign(_optionsPublic, _optionsPrivate); 29 | 30 | options.htmlSelector = document.querySelector(htmlSelector); 31 | } 32 | 33 | /** 34 | * Запуск игры 35 | */ 36 | Battlefield.prototype.run = function () { 37 | try { 38 | var F = new Field(); 39 | var fields = F.setBarrier(); 40 | 41 | new Battle(fields, this); 42 | } catch (err) { 43 | h.showExceptions(err); 44 | } 45 | }; 46 | 47 | /** 48 | * Вернет настройки для указанного уровеня сложности 49 | * 50 | * @param type 51 | * @returns {*} 52 | */ 53 | Battlefield.prototype.getLevel = function (type) { 54 | var level = { 55 | easy: { 56 | fSize: {h: 10, v: 10}, 57 | fBarrier: [[4, 1], [3, 2], [2, 3], [1, 4]] 58 | }, 59 | middle: { 60 | fSize: {h: 15, v: 15}, 61 | fBarrier: [[5, 2], [4, 3], [3, 4], [2, 5], [1, 6]] 62 | }, 63 | hard: { 64 | fSize: {h: 15, v: 20}, 65 | fBarrier: [[6, 1], [5, 2], [4, 3], [3, 4], [2, 5], [1, 6]] 66 | } 67 | }; 68 | 69 | if (typeof level[type] == 'object') { 70 | return level[type]; 71 | } 72 | else throw new Error(h.getMessage('err_invalid_level')); 73 | }; 74 | 75 | /** 76 | * Установит уровень сложности 77 | * 78 | * @param type 79 | * @returns {Battlefield} 80 | */ 81 | Battlefield.prototype.setLevel = function (type) { 82 | var nLevel = this.getLevel(type); 83 | 84 | options.fSize = nLevel.fSize; 85 | options.fBarrier = nLevel.fBarrier; 86 | 87 | _globalLevel = type; 88 | 89 | return this; 90 | }; 91 | 92 | /** 93 | * Выведет модальное окно для изменения настроек игры 94 | */ 95 | Battlefield.prototype.updateConfig = function () { 96 | var self = this; 97 | 98 | var contentHtml = 99 | '' + 100 | ' ' + 101 | ' ' + 102 | ' ' + 108 | ' ' + 109 | ' ' + 110 | 111 | ' ' + 112 | ' ' + 113 | ' ' + 117 | ' ' + 118 | 119 | ' ' + 120 | ' ' + 121 | ' ' + 129 | ' ' + 130 | 131 | '
Уровень сложности:' + 103 | ' ' + 104 | ' ' + 105 | ' ' + 106 | ' ' + 107 | '
Размер поля:' + 114 | ' ' + 115 | ' ' + 116 | '
Корабли на поле' + 122 | ' ' + 123 | ' ' + 124 | ' ' + 125 | ' ' + 126 | ' ' + 127 | ' ' + 128 | '
'; 132 | contentHtml = '
' + contentHtml + '
'; 133 | 134 | var newLevel = _globalLevel; 135 | 136 | h.modalWindow(h.getMessage('options'), contentHtml, [{ 137 | elValue: h.getMessage('new_game'), 138 | onClick: function (modal) { 139 | modal.close(); 140 | 141 | try { 142 | if (newLevel == 'user') { 143 | var nOpt = getUserOptions(); 144 | 145 | options.fSize = nOpt.fSize; 146 | options.fBarrier = nOpt.fBarrier; 147 | 148 | _globalLevel = newLevel; 149 | } else { 150 | if (newLevel.length > 0 && newLevel != _globalLevel) 151 | self.setLevel(newLevel); 152 | } 153 | 154 | self.run(); 155 | } catch (err) { 156 | h.showExceptions(err); 157 | } 158 | } 159 | },{ 160 | elValue: h.getMessage('set_default_params'), 161 | elClass: 'btn-warning', 162 | onClick: function (modal) { 163 | modal.close(); 164 | 165 | self.setLevel('middle'); 166 | self.run(); 167 | } 168 | },{ 169 | elValue: h.getMessage('close'), 170 | elClass: 'btn-danger', 171 | onClick: function (modal) { 172 | modal.close(); 173 | } 174 | } 175 | ]); 176 | 177 | var table = document.querySelector('.bf-config'); 178 | var btnList = document 179 | .getElementById('config_level') 180 | .querySelectorAll('button'); 181 | 182 | activeFormOptions(_globalLevel == 'user'); 183 | echoOptions(options); 184 | 185 | // выбор уровня сложности 186 | btnList.forEach(function (btn) { 187 | btn.onclick = function (env) { 188 | 189 | newLevel = env.target.value; 190 | 191 | btnList.forEach(function (b) { 192 | b.classList.remove('active'); 193 | }); 194 | env.target.classList += ' active'; 195 | 196 | if (newLevel == 'user') { 197 | activeFormOptions(true); 198 | } else { 199 | activeFormOptions(false); 200 | echoOptions(self.getLevel(newLevel)); 201 | } 202 | 203 | return false; 204 | } 205 | }); 206 | 207 | // private method.................... 208 | // .................................. 209 | 210 | // активирует форму для ввода пользовательских настроек 211 | function activeFormOptions(active) { 212 | // размер игрового поля 213 | table.querySelector('#opt-fsize').querySelectorAll('label') 214 | .forEach(function (label) { 215 | var input = label.querySelector('input'); 216 | 217 | if (active) { 218 | label.classList = ''; 219 | input.disabled = false; 220 | } else { 221 | label.classList = 'no-act'; 222 | input.disabled = true; 223 | } 224 | }); 225 | 226 | // корабли на поле 227 | table.querySelector('#opt-fbarrier').querySelectorAll('label') 228 | .forEach(function (label) { 229 | var input = label.querySelector('input'); 230 | 231 | if (active) { 232 | label.classList = ''; 233 | input.disabled = false; 234 | } else { 235 | label.classList = 'no-act'; 236 | input.disabled = true; 237 | } 238 | }); 239 | } 240 | 241 | // вернет настройки пользователя 242 | function getUserOptions() { 243 | var nOptions = { 244 | fSize: {h: 0, v: 0}, 245 | fBarrier: [] 246 | }; 247 | 248 | // размер игрового поля 249 | nOptions.fSize.h = parseInt(table.querySelector('input#type-h').value); 250 | nOptions.fSize.v = parseInt(table.querySelector('input#type-v').value); 251 | 252 | // корабли на игровом поле 253 | table.querySelector('#opt-fbarrier').querySelectorAll('label') 254 | .forEach(function (label) { 255 | var input = label.querySelector('input'), 256 | ship = parseInt(input.getAttribute('bar')), 257 | ctn = parseInt(input.value); 258 | 259 | if (ctn > 0) 260 | nOptions.fBarrier.push([ship, ctn]); 261 | }); 262 | 263 | return nOptions; 264 | } 265 | 266 | // напечатает переданные настройки 267 | function echoOptions(nOptions) { 268 | // размер игрового поля 269 | table.querySelector('input#type-h').value = nOptions.fSize.h; 270 | table.querySelector('input#type-v').value = nOptions.fSize.v; 271 | 272 | // корабли на игровом поле 273 | table.querySelector('#opt-fbarrier').querySelectorAll('input').forEach(function (input) { 274 | input.value = 0; 275 | }); 276 | 277 | nOptions.fBarrier.forEach(function (barrier) { 278 | var ship = barrier[0], // кол-во палуб корабля 279 | ctn = barrier[1]; // кол-во кораблей 280 | 281 | table.querySelector('#opt-fbarrier').querySelector('input#bar_' + ship).value = ctn; 282 | }); 283 | } 284 | }; -------------------------------------------------------------------------------- /src/js/battlefield.class-battle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Запускает алгоритмы обстрела игроками поля боя 3 | * 4 | * Требуется: 5 | * options = {} - глобальный объект с опциями игры 6 | * h = {} - глобальный обьект функций помошников 7 | * 8 | * @param fields - обьект массивов игровых поле 9 | * @param battlefield - экземпляр Battlefield 10 | * @constructor 11 | */ 12 | function Battle(fields, battlefield) { 13 | if (!(battlefield instanceof Battlefield)) 14 | throw new Error('Invalid Battlefield object in the construct Battle'); 15 | 16 | GameUI.apply(this, arguments); 17 | 18 | this.fields = fields; 19 | this.battlefield = battlefield; 20 | 21 | this._lsKill = []; // последнее попадание компьютером 22 | this._lsShot = []; // список точек доступных для выстрела 23 | this._ctnCell = []; // остаток кораблей у игрока 24 | 25 | for (var p in options.player) { // заполнение данных 26 | var v = options.player[p]; 27 | 28 | this._lsKill[v] = []; 29 | this._lsShot[v] = getListPoint(); 30 | this._ctnCell[v] = getCountCell(); 31 | } 32 | this.userKey = options.player.kUser; // ключ игрока 33 | this.enemyKey = options.player.kBrain; // ключ компьютера 34 | 35 | // первый ход 36 | this.player = h.rand(1, 2) % 2 ? this.userKey : this.enemyKey; 37 | this.game(this.player); 38 | 39 | 40 | // private method.................... 41 | // .................................. 42 | 43 | function getListPoint() { 44 | var __fullPoint = []; 45 | for (var i = 0; i < options.fSize.h; i++) { 46 | for (var j = 0; j < options.fSize.v; j++) 47 | __fullPoint.push([j, i]); 48 | } 49 | return __fullPoint; 50 | } 51 | 52 | function getCountCell() { 53 | var __countCell = 0; 54 | options.fBarrier.forEach(function (ship) { 55 | __countCell += ship[0] * ship[1]; 56 | }); 57 | return __countCell; 58 | } 59 | } 60 | 61 | Battle.prototype = Object.create(GameUI.prototype); // наследование 62 | 63 | /** 64 | * Осуществляет переход хода 65 | * 66 | * @param fKey - строковый ключ игрока 67 | */ 68 | Battle.prototype.game = function (fKey) { 69 | try { 70 | this.showProgress(fKey); 71 | this.player = fKey; 72 | 73 | switch (fKey) { 74 | case (this.userKey): 75 | this.shotUser(); 76 | break; 77 | case (this.enemyKey): 78 | this.shotAI(this.userKey); 79 | break; 80 | default: 81 | throw new Error(h.getMessage('err_invalid_player')); 82 | } 83 | } catch (err) { 84 | h.showExceptions(err); 85 | } 86 | }; 87 | 88 | /** 89 | * Ход игрока, 90 | * ожидает событие клика по игровому полю 91 | */ 92 | Battle.prototype.shotUser = function () { 93 | var self = this; 94 | 95 | this.clickToField(this.enemyKey, function (event) { 96 | if (self.player !== self.userKey) 97 | return false; 98 | 99 | try { 100 | var Y = event.target.parentNode.cellIndex, 101 | X = event.target.parentNode.parentNode.rowIndex; 102 | 103 | self.shot([X, Y], self.enemyKey); 104 | } catch (err) { 105 | self.game(self.userKey); 106 | } 107 | }); 108 | }; 109 | 110 | /** 111 | * Ход компьютера, 112 | * в зависимости от попадания по кораблю противника будет 113 | * добивать корабль или стрелять по случайной клетке 114 | * 115 | * @param fKey - строковый ключ игрока 116 | */ 117 | Battle.prototype.shotAI = function (fKey) { 118 | var self = this, 119 | field = this.fields[fKey], 120 | fSize = options.fSize, 121 | tPoint = options.tPoint, 122 | point = getPointShot(); 123 | 124 | setTimeout(function () { 125 | self.shot(point, fKey); 126 | }, 600); 127 | 128 | 129 | // private method.................... 130 | // .................................. 131 | 132 | // вернет случайную точку для выстрела 133 | function getPointShot() { 134 | var point = []; 135 | 136 | if (self._lsKill[fKey].length > 0) { // если ранее попадали по кораблю 137 | point = getPointFinishing(); 138 | } else { // случайный выстрел 139 | var rInx = h.rand(0, self._lsShot[fKey].length - 1); 140 | point = self._lsShot[fKey][rInx]; // получаем случайную точку 141 | self._lsShot[fKey].splice(rInx, 1); // удаляем точку из доступных для выстрела 142 | } 143 | 144 | // проверка полученной точки... 145 | if (point instanceof Array && point[0] !== 'undefined' && point[1] !== 'undefined') { 146 | var X = point[0], Y = point[1]; 147 | if (X >= 0 && X < options.fSize.v && Y >= 0 && Y < options.fSize.h) { 148 | if (field[X][Y] == tPoint.NUL || field[X][Y] == tPoint.KIL) { 149 | // уже стреляли в эту точку 150 | } 151 | else return point; 152 | } 153 | } 154 | 155 | return getPointShot(); 156 | } 157 | 158 | // вернет точку для добивания 159 | function getPointFinishing() { 160 | var cP = [[-1, 0], [0, -1], [1, 0], [0, 1]], // возможные направления для добивания 161 | lsP = self._lsKill[fKey], 162 | point = []; 163 | 164 | var i = 0, n = 0; 165 | 166 | if (lsP.length == 1) { // первый выстрел при добивание 167 | var X = lsP[0][0], Y = lsP[0][1]; 168 | 169 | cP = h.shuffle(cP); // перемешает массив 170 | for (i = 0; i < cP.length; i++) { // выбираем случайное направление для выстрела 171 | var pX = X + cP[i][0], 172 | pY = Y + cP[i][1]; 173 | 174 | // точка в рамках поля и туда еще не стреляли 175 | if (pX >= 0 && pX < fSize.v && pY >= 0 && pY < fSize.h) { 176 | if (field[pX][pY] !== tPoint.NUL) 177 | return [pX, pY]; 178 | } 179 | } 180 | } else { // второй и т.д выстрел при добивании 181 | var posHorizontal = lsP[0][0] == lsP[1][0], // определяем направление корабля 182 | min = fSize.h + fSize.v, max = 0; // крайние точки 183 | 184 | for (i = 0; i < lsP.length; i++) { // поиск крайних точек корабля 185 | n = posHorizontal ? lsP[i][1] : lsP[i][0]; 186 | 187 | min = min > n ? n : min; 188 | max = max < n ? n : max; 189 | } 190 | 191 | // случайным образом определяем с какого края стрелять по караблю 192 | var nP = h.rand(1, 2) % 2 ? min - 1 : max + 1; 193 | point = posHorizontal ? [lsP[0][0], nP] : [nP, lsP[0][1]]; 194 | } 195 | 196 | return point; 197 | } 198 | }; 199 | 200 | /** 201 | * Осуществляет выстрел по игровому полю 202 | * 203 | * @param point - массив 2 элементов с точкой на игровом поле 204 | * @param fKey - строковый ключ игрока 205 | */ 206 | Battle.prototype.shot = function (point, fKey) { 207 | var self = this, 208 | ctnCellShip = 0, 209 | _fKey = fKey == this.userKey ? this.enemyKey : this.userKey; 210 | var X = point[0], Y = point[1]; 211 | 212 | var check = checkPoint(point, fKey); // проверка выстрела 213 | if (typeof check == 'boolean') { // во что то попал 214 | if (check) { // попал на по кораблю -> РАНИЛ 215 | this._ctnCell[fKey]--; // минус 1 точка для выстрела 216 | 217 | this.fields[fKey][X][Y] = options.tPoint.KIL; 218 | var isKill = isKillShip(point, fKey); 219 | if (typeof isKill == 'boolean') { // корабль просто ранен 220 | this._lsKill[fKey].push(point); // сохраняем последнее попадание 221 | 222 | this.setMarker(point, fKey, options.tPoint.KIL); 223 | this.setHelpMarker(point, fKey, function (_point) {// при ранении установим точки подсказки 224 | var _X = _point[0], 225 | _Y = _point[1]; 226 | self.fields[fKey][_X][_Y] = options.tPoint.NUL; 227 | }); 228 | } 229 | else { // корабль убит 230 | this._lsKill[fKey] = []; // очищаем список последнего попадания 231 | 232 | isKill.forEach(function (_point) { 233 | var _X = _point[0], 234 | _Y = _point[1]; 235 | 236 | // ставим метки на игровое поле, если разрешены подсказки 237 | if (options.printHelp) { 238 | self.setMarker(_point, fKey, options.tPoint.NUL, true); 239 | self.fields[fKey][_X][_Y] = options.tPoint.NUL; 240 | } 241 | if (_fKey == options.player.kBrain) 242 | self.fields[fKey][_X][_Y] = options.tPoint.NUL; 243 | }); 244 | 245 | this.setMarker(point, fKey, options.tPoint.KIL + '_death'); 246 | this.shipInfoMap(ctnCellShip, fKey); 247 | } 248 | 249 | if (this._ctnCell[fKey] === 0) { // конец игры 250 | this.player = false; 251 | this.gameOver(_fKey); 252 | } else this.game(_fKey); // повтор хода 253 | } 254 | else { // попадание по пустой клетке -> МИМО 255 | this.fields[fKey][X][Y] = options.tPoint.NUL; 256 | this.setMarker(point, fKey, options.tPoint.NUL); 257 | this.game(fKey); // переход кода 258 | } 259 | } 260 | else this.game(_fKey); // уже стрелял в эту точку -> повтор хода 261 | 262 | 263 | // private method.................... 264 | // .................................. 265 | 266 | // проверяет точку выстрела по полю 267 | function checkPoint(point, fKey) { 268 | var X = point[0], Y = point[1]; 269 | 270 | switch (self.fields[fKey][X][Y]) { 271 | case (options.tPoint.DEF): // мимо 272 | return false; 273 | case (options.tPoint.BAR): // ранил 274 | return true; 275 | default: // что то другое 276 | return null; 277 | } 278 | } 279 | 280 | // проверка уничтожения корабля 281 | function isKillShip(point, fKey) { 282 | var cP = [[-1, 0], [-1, -1], [0, -1], [1, -1], [1, 0], [1, 1], [0, 1], [-1, 1]], 283 | field = self.fields[fKey], 284 | sPoints = [], 285 | lsCellShip = []; 286 | 287 | if (_checkPointToKill(point)) { 288 | lsCellShip.push(point); 289 | ctnCellShip = lsCellShip; 290 | 291 | return sPoints; 292 | } 293 | else return false; 294 | 295 | 296 | // private method.................... 297 | // .................................. 298 | 299 | // убит или нет корабль, если да то заполнет массив соседних точек 300 | function _checkPointToKill(point, noCheck) { 301 | var X = point[0], Y = point[1], // исходная проверяемая точка 302 | nX = false, nY = false; // не проверяемая точка при повторе 303 | var dopCheck = []; // дополнительные точки проверки 304 | 305 | if (typeof noCheck == "object") { // не проверяемая точка, при рекурсии 306 | nX = noCheck[0]; 307 | nY = noCheck[1]; 308 | } 309 | 310 | for (var i = 0; i < cP.length; i++) { // перебор точек по кругу 311 | var pX = X + cP[i][0], 312 | pY = Y + cP[i][1]; 313 | 314 | if (pX === nX && pY === nY) { // эту точку уже проверили 315 | // точка проверенна при предыдущей итерации 316 | } else { 317 | // только если точка в рамках игрового поля 318 | if (pX >= 0 && pX < options.fSize.v && pY >= 0 && pY < options.fSize.h) { 319 | var val = field[pX][pY]; 320 | 321 | if (val == options.tPoint.BAR) 322 | return false; // целая часть корабля -> не убит 323 | else if (val == options.tPoint.KIL) { // раненая часть корабля, запомним для рекурсии 324 | lsCellShip.push([pX, pY]); // подсчет палуб 325 | dopCheck.push([[pX, pY], [X, Y]]); // доп.точка првоерки 326 | } 327 | else sPoints.push([pX, pY]); // подбитая часть корабля, 328 | } 329 | } 330 | } 331 | 332 | // доп.точки проверки, если еще есть то запуск рекурсии 333 | if (dopCheck.length === 0) 334 | return true; // точек более нет -> убит 335 | else { 336 | var sRes = 0; 337 | for (var k = 0; k < dopCheck.length; k++) { 338 | if (!_checkPointToKill(dopCheck[k][0], dopCheck[k][1])) 339 | return false; 340 | else sRes++; 341 | } 342 | return sRes == dopCheck.length; // проверенно == доп.точек 343 | } 344 | } 345 | } 346 | }; -------------------------------------------------------------------------------- /src/js/battlefield.class-gameui.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Класс управления пользовательским интерфейсом 3 | * 4 | * Требуется: 5 | * options = {} - глобальный объект с опциями игры 6 | * h = {} - глобальный обьект функций помошников 7 | * 8 | * @param htmlSelector - document.querySelector 9 | * @constructor 10 | */ 11 | function GameUI(fields, battlefield) { 12 | if (options.htmlSelector == null) 13 | throw new Error(h.getMessage('err_invalid_selector')); 14 | 15 | this.fields = fields; 16 | this.battlefield = battlefield; 17 | 18 | var self = this; 19 | var playerName = 20 | '
' + 21 | ' ' + h.getMessage('new_game') + '' + 22 | ' ' + h.getMessage('options') + '' + 23 | '
'; 24 | 25 | // очистка блока 26 | options.htmlSelector.innerHTML = ''; 27 | 28 | // имена игроков 29 | if (options.printName) { 30 | var echoTotal = options.winner.kUser + options.winner.kBrain != 0; 31 | playerName += 32 | '
' + 33 | '' + h.getPlayerName(options.player.kUser) + '' + 34 | (echoTotal ? '' + options.winner.kUser + '' : '') + 35 | '&' + 36 | (echoTotal ? '' + options.winner.kBrain + '' : '') + 37 | ' ' + h.getPlayerName(options.player.kBrain) + '' + 38 | '
'; 39 | } 40 | 41 | this.nameHtml = document.createElement('div'); 42 | this.nameHtml.setAttribute('class', 'bf-player-name'); 43 | this.nameHtml.innerHTML = playerName; 44 | options.htmlSelector.appendChild(this.nameHtml); 45 | 46 | // игровые поля 47 | this.fieldHtml = document.createElement('div'); 48 | this.fieldHtml.setAttribute('class', 'bf-fields'); 49 | this.fieldHtml.innerHTML = this.getFieldHTML(options.player); 50 | options.htmlSelector.appendChild(this.fieldHtml); 51 | 52 | // логирование боя 53 | if (options.printLog) { 54 | var filter = 55 | '
' + 56 | ' ' + h.getMessage('filter') + ':' + 57 | ' ' + h.getPlayerName(options.player.kUser) + '' + 58 | ' ' + h.getPlayerName(options.player.kBrain) + '' + 59 | ' ' + h.getMessage('full') + '' + 60 | '
'; 61 | 62 | this.logFilter = null; 63 | this.actFilter = ''; 64 | 65 | this.logHtml = document.createElement('div'); 66 | this.logHtml.setAttribute('class', 'bf-logger'); 67 | this.logHtml.innerHTML = '

' + h.getMessage('info_log_title') + filter + '

'; 68 | options.htmlSelector.appendChild(this.logHtml); 69 | } 70 | 71 | 72 | document.getElementById('new_game').onclick = function () { 73 | self.battlefield.run(); 74 | }; 75 | document.getElementById('config').onclick = function () { 76 | self.battlefield.updateConfig(); 77 | }; 78 | 79 | } 80 | 81 | /** 82 | * Вернет HTML разметку игрового поля 83 | * 84 | * @param player - строковый ключ игрока 85 | * @returns {string} 86 | */ 87 | GameUI.prototype.getFieldHTML = function (player) { 88 | var fields = this.fields, 89 | html = ''; 90 | 91 | for (var k in player) { 92 | var fKey = player[k], 93 | dopAttr = fKey == options.player.kUser ? 'lf' : 'rg', 94 | printShip = dopAttr == 'lf'; 95 | 96 | html += '
'; 97 | html += getFieldTable(fKey, printShip); 98 | html += getBarrierInfo(fKey); 99 | html += '
'; 100 | } 101 | 102 | return html; 103 | 104 | 105 | // private method.................... 106 | // .................................. 107 | 108 | // вернет HTML игрового поля 109 | function getFieldTable(fKey, printShip) { 110 | printShip = printShip || false; 111 | var table = ''; 112 | 113 | table += ''; 114 | for (var x = 0; x < options.fSize.v; x++) { 115 | table += ''; 116 | for (var y = 0; y < options.fSize.h; y++) { 117 | var mm = '', ll = ''; 118 | 119 | // установка маркеров 120 | if (options.fMarker !== false && typeof options.fMarker == 'object') { 121 | var txtMM = typeof options.fMarker.h != 'undefined' ? (options.fMarker.h == 'char' ? h.getLetter(y) : (y + 1)) : (y + 1), 122 | txtLL = typeof options.fMarker.v != 'undefined' ? (options.fMarker.v == 'char' ? h.getLetter(x) : (x + 1)) : (x + 1); 123 | 124 | mm = x === 0 ? '
' + txtMM + '
' : ''; 125 | ll = y === 0 ? '
' + txtLL + '
' : ''; 126 | } 127 | 128 | var ship = ''; 129 | if (printShip) 130 | ship = fields[fKey][x][y] == options.tPoint.BAR ? ' let' : ''; 131 | table += ''; 132 | } 133 | table += ''; 134 | } 135 | table += '
' + mm + ll + '
'; 136 | 137 | return table; 138 | } 139 | 140 | // вернет HTML списка кораблей 141 | function getBarrierInfo(fKey) { 142 | var html = ''; 143 | 144 | options.fBarrier.forEach(function (ship) { 145 | var box = ''; 146 | for (var c = 0; c < ship[0]; c++) 147 | box += '
'; 148 | html += 149 | '
' + 150 | ' x' + ship[1] + '' + 151 | ' ' + box + 152 | '
'; 153 | }); 154 | 155 | return '
' + html + '
'; 156 | } 157 | }; 158 | 159 | /** 160 | * Оповещает пользователя о переходе хода, 161 | * блокирует игровое поле ожидающего 162 | * 163 | * @param fKey строковый ключ игрока 164 | * @returns {*} 165 | */ 166 | GameUI.prototype.showProgress = function (fKey) { 167 | // в конце игры, блокируем игровые поля и убираем метку активности с имени 168 | if (!fKey) { 169 | this.fieldHtml.querySelectorAll('table.field').forEach(function (table) { 170 | table.classList.add('timeout'); 171 | }); 172 | 173 | if (options.printName) { 174 | this.nameHtml.querySelectorAll('.name').forEach(function (span) { 175 | span.classList.remove('act'); 176 | }); 177 | } 178 | return false; 179 | } 180 | 181 | // делаем игровое поле неактивным 182 | this.fieldHtml.querySelectorAll('table.field.timeout').forEach(function (table) { 183 | table.classList.remove('timeout'); 184 | }); 185 | this.fieldHtml.querySelector('table.field#' + fKey).classList.add('timeout'); 186 | 187 | if (!options.printName) 188 | return null; 189 | 190 | // ставим метку к кому перешел ход 191 | this.nameHtml.querySelectorAll('.name').forEach(function (span) { 192 | span.classList.remove('act'); 193 | }); 194 | this.nameHtml.querySelector('.name#' + fKey).classList.add('act'); 195 | }; 196 | 197 | /** 198 | * Вешает событие ожидания клика по игровому полю 199 | * 200 | * @param fKey - строковый ключ игрока 201 | * @param callback - фукция 202 | */ 203 | GameUI.prototype.clickToField = function (fKey, callback) { 204 | this.fieldHtml.querySelector('table.field#' + fKey).onclick = callback; 205 | }; 206 | 207 | /** 208 | * Печатает маркер на игровом поле 209 | * 210 | * @param point - точка в формате [x, y] 211 | * @param fKey - строковый ключ игрока 212 | * @param tPoint - тип точки 213 | * @param auto - автоматическая точка 214 | * @returns {null} 215 | */ 216 | GameUI.prototype.setMarker = function (point, fKey, tPoint, auto) { 217 | var X = point[0], 218 | Y = point[1], 219 | elClass = ''; 220 | auto = auto || false; 221 | 222 | switch (tPoint) { 223 | case (options.tPoint.NUL): // мимо 224 | elClass = auto ? 'auto-null' : 'null'; 225 | break; 226 | case (options.tPoint.KIL): // ранил 227 | elClass = 'kill'; 228 | break; 229 | case (options.tPoint.KIL + '_death'): // убил 230 | elClass = 'kill'; 231 | break; 232 | default: 233 | return null; 234 | } 235 | 236 | if (!auto) this.printLog(point, fKey, tPoint); 237 | 238 | var coll = this.fieldHtml.querySelector('table.field#' + fKey) 239 | .rows[X].cells[Y]; 240 | coll.querySelector('.box').className += ' ' + elClass; 241 | 242 | if (!auto) { 243 | var shotBox = document.createElement('div'); 244 | shotBox.setAttribute('class', 'shot'); 245 | coll.appendChild(shotBox); 246 | 247 | h.animateOpacity(shotBox, 2000); 248 | } 249 | }; 250 | 251 | /** 252 | * Ставит точки подсказки по углам от подбитого корабля 253 | * 254 | * @param point - точка в формате [x, y] 255 | * @param fKey - строковый ключ игрока 256 | * @param callback - фукция запускается после установки точки 257 | * @returns {boolean} 258 | */ 259 | GameUI.prototype.setHelpMarker = function (point, fKey, callback) { 260 | if (!options.printHelp) 261 | return false; 262 | 263 | var sP = [[-1, -1], [1, -1], [1, 1], [-1, 1]], 264 | X = point[0], Y = point[1]; 265 | 266 | for (var i = 0; i < sP.length; i++) { 267 | var nX = X + sP[i][0], 268 | nY = Y + sP[i][1]; 269 | 270 | if (nX >= 0 && nX < options.fSize.v && nY >= 0 && nY < options.fSize.h) { 271 | this.setMarker([nX, nY], fKey, options.tPoint.NUL, true); 272 | if (typeof callback == 'function') 273 | callback([nX, nY], fKey); 274 | } 275 | } 276 | }; 277 | 278 | /** 279 | * Информирует пользователя об оставшихся кораблях на игровом поле 280 | * 281 | * @param ctnCell - массив точек убитого корабля 282 | * @param fKey - строковый ключ игрока 283 | */ 284 | GameUI.prototype.shipInfoMap = function (ctnCell, fKey) { 285 | var count = ctnCell.length; 286 | 287 | var letBox = this.fieldHtml 288 | .querySelector('.list-let#' + fKey) 289 | .querySelector('#cell_' + count); 290 | 291 | var span = letBox.querySelector('span'), 292 | dCtn = parseInt(span.getAttribute('data-ctn')) - 1; 293 | 294 | span.setAttribute('data-ctn', dCtn); 295 | span.innerHTML = 'x' + dCtn; 296 | 297 | var shot = document.createElement('div'); 298 | shot.setAttribute('class', 'shot'); 299 | span.appendChild(shot); 300 | 301 | h.animateOpacity(shot, 3000); 302 | 303 | // если кораблей такова типа больше нет 304 | if (dCtn == 0) { 305 | letBox.querySelectorAll('div').forEach(function (box) { 306 | box.className += ' kill'; // ставим метку убит 307 | }); 308 | } 309 | 310 | // если корабль убит, делаем его крассным 311 | var table = this.fieldHtml.querySelector('.field#' + fKey); 312 | ctnCell.forEach(function (ship) { 313 | var X = ship[0], 314 | Y = ship[1]; 315 | 316 | table.rows[X].cells[Y] 317 | .querySelector('.box') 318 | .className += ' death'; 319 | }); 320 | }; 321 | 322 | /** 323 | * Ведет журнал боя, 324 | * так же запускает фильтр журнала и подсветку точек на игром поле при наведении на запись 325 | * 326 | * @param point - точка выстрела в формате [x, y] 327 | * @param fKey - строковый ключ игрока 328 | * @param tPoint - тип попадания 329 | * @returns {boolean} 330 | */ 331 | GameUI.prototype.printLog = function (point, fKey, tPoint) { 332 | if (!options.printLog) 333 | return false; 334 | 335 | var self = this; 336 | 337 | var html = '', 338 | X = point[0], Y = point[1]; 339 | 340 | var tPoint_class = '', 341 | tPoint_name = ''; 342 | 343 | var player = fKey == options.player.kUser 344 | ? h.getPlayerName(options.player.kBrain) : h.getPlayerName(options.player.kUser), 345 | __date = new Date(), 346 | time = __date.toLocaleTimeString(), 347 | marker = ''; 348 | 349 | switch (tPoint) { 350 | case (options.tPoint.KIL): 351 | tPoint_class = 'war'; 352 | tPoint_name = 'log_kill'; 353 | break; 354 | case (options.tPoint.KIL + '_death'): 355 | tPoint_class = 'kil'; 356 | tPoint_name = 'log_death'; 357 | break; 358 | case (options.tPoint.NUL): 359 | tPoint_class = 'nul'; 360 | tPoint_name = 'log_past'; 361 | break; 362 | } 363 | 364 | marker += options.fMarker.v == 'char' ? h.getLetter(X) : (X + 1); 365 | marker += "x"; 366 | marker += options.fMarker.h == 'char' ? h.getLetter(Y) : (Y + 1); 367 | 368 | html += 369 | '' + marker + '' + 370 | '' + h.getMessage(tPoint_name) + '' + 371 | '' + player + '' + 372 | '' + time + ''; 373 | 374 | var li = document.createElement('li'); 375 | li.setAttribute('id', fKey); 376 | li.setAttribute('data-x', X); 377 | li.setAttribute('data-y', Y); 378 | 379 | if (this.actFilter == fKey) 380 | li.setAttribute('class', 'no-active'); 381 | 382 | li.innerHTML = html; 383 | 384 | this.logHtml.querySelector('ul').insertBefore(li, this.logHtml.querySelector('ul').firstChild); 385 | 386 | // при наведении на лог, подсветка точки игрового поля 387 | li.onmouseover = function (event) { 388 | if (event.target.nodeName != 'LI') 389 | return false; 390 | 391 | var fKey = event.target.getAttribute('id'), // event.target.dataset.fkey, 392 | X = event.target.getAttribute('data-x'), // event.target.dataset.x, 393 | Y = event.target.getAttribute('data-y'); // event.target.dataset.y; 394 | 395 | var td = self.fieldHtml.querySelector('.field#' + fKey).rows[X].cells[Y], 396 | shot = document.createElement('div'); 397 | 398 | shot.setAttribute('class', 'shot'); 399 | td.appendChild(shot); 400 | 401 | li.onmouseout = function () { 402 | h.animateOpacity(shot, 1000); 403 | }; 404 | }; 405 | 406 | // фильт журнала 407 | if (this.logFilter == null) { 408 | this.logFilter = this.logHtml.querySelector('.bf-filter'); 409 | this.logFilter.classList = 'bf-filter'; 410 | 411 | var activeFilter = this.logFilter.querySelector('#full'); 412 | this.logFilter.querySelectorAll('span').forEach(function (span) { 413 | span.onclick = function (event) { 414 | if (activeFilter == event.target) 415 | return false; 416 | 417 | if (activeFilter) 418 | activeFilter.classList.remove('active'); 419 | 420 | self.actFilter = event.target.getAttribute('id'); 421 | 422 | activeFilter = event.target; 423 | activeFilter.classList.add('active'); 424 | 425 | self.logHtml.querySelectorAll('li').forEach(function (li) { 426 | li.classList = self.actFilter == li.getAttribute('id') ? 'no-active' : ''; 427 | }); 428 | } 429 | }) 430 | } 431 | }; 432 | 433 | /** 434 | * Показывает корабли на игровом поле 435 | * 436 | * @param fKey - строковый ключ игрока 437 | */ 438 | GameUI.prototype.showShip = function (fKey) { 439 | var field = this.fields[fKey], 440 | table = this.fieldHtml.querySelector('table#' + fKey); 441 | 442 | for (var X = 0; X < field.length; X++) { 443 | for (var Y = 0; Y < field[X].length; Y++) 444 | if (field[X][Y] == options.tPoint.BAR) { 445 | table 446 | .rows[X].cells[Y] 447 | .querySelector('.box') 448 | .className += ' let'; 449 | } 450 | } 451 | }; 452 | 453 | /** 454 | * Оповещает пользователя об окончании игры, 455 | * выводит модальное окно 456 | * 457 | * @param winner - строковый ключ игрока, победителя 458 | */ 459 | GameUI.prototype.gameOver = function (winner) { 460 | var self = this, 461 | kWinner = options.player.kUser == winner ? 'kUser' : 'kBrain'; 462 | var title = '', 463 | elClass = '', 464 | message = ''; 465 | 466 | // счетчик побед... 467 | options.winner[kWinner]++; 468 | 469 | if (options.player.kUser == winner) { 470 | elClass = 'winner'; 471 | title = h.getMessage('you_winner'); 472 | message = h.getMessage('info_winner'); 473 | } else { 474 | this.showShip(winner); 475 | 476 | elClass = 'lose'; 477 | title = h.getMessage('you_lose'); 478 | message = h.getMessage('info_loser'); 479 | } 480 | 481 | var contentHtml = 482 | '
' + 483 | '
' + 484 | '

' + title + '

' + 485 | '

' + message + '

' + 486 | '
'; 487 | 488 | // выводит модальное окно с опциями 489 | h.modalWindow(h.getMessage('game_over'), contentHtml, [{ 490 | elValue: h.getMessage('new_game'), 491 | onClick: function (modal) { 492 | modal.close(); 493 | self.battlefield.run(); 494 | } 495 | }, { 496 | elValue: h.getMessage('update_options'), 497 | elClass: 'btn-warning', 498 | onClick: function (modal) { 499 | modal.close(); 500 | self.showProgress(false); 501 | self.battlefield.updateConfig(); 502 | } 503 | }, { 504 | elValue: h.getMessage('close'), 505 | elClass: 'btn-danger', 506 | onClick: function (modal) { 507 | modal.close(); 508 | self.showProgress(false); 509 | } 510 | }]); 511 | }; -------------------------------------------------------------------------------- /dist/battlefield.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * battlefield-js v1.0.1 3 | * This is classic game Battlefield for browsers, implemented on language JavaScript. 4 | * 5 | * repository: git+https://github.com/alx2das/battlefield-js.git 6 | * bugs: git+https://github.com/alx2das/battlefield-js.git 7 | * homepage: https://alx2das.github.io/battlefield-js/examples/ 8 | * 9 | * Copyright 2017, Alexandr Builov 10 | * Date: Sun Feb 05 2017 11 | */ 12 | !function(e){"use strict";function t(e,r){if(o instanceof t)return o;if(o=this,"object"==typeof r){for(var i in l)n[i]=r.hasOwnProperty(i)?r[i]:l[i];n=Object.assign(n,s)}else n=Object.assign(l,s);n.htmlSelector=document.querySelector(e)}function r(){if(n.fSize.h<10||n.fSize.h>25||n.fSize.v<10||n.fSize.v>25)throw new RangeError(f.getMessage("err_size_field"));if(!(n.fBarrier instanceof Array)||n.fBarrier.length<1)throw new RangeError(f.getMessage("err_barrier"));this.fields=[],this.fName=[n.player.kUser,n.player.kBrain];for(var e in n.player){for(var t=n.player[e],r=new Array(n.fSize.v),i=0;i '+f.getMessage("new_game")+' '+f.getMessage("options")+"";if(n.htmlSelector.innerHTML="",n.printName){var a=n.winner.kUser+n.winner.kBrain!=0;i+='
'+f.getPlayerName(n.player.kUser)+""+(a?''+n.winner.kUser+"":"")+"&"+(a?''+n.winner.kBrain+"":"")+' '+f.getPlayerName(n.player.kBrain)+"
"}if(this.nameHtml=document.createElement("div"),this.nameHtml.setAttribute("class","bf-player-name"),this.nameHtml.innerHTML=i,n.htmlSelector.appendChild(this.nameHtml),this.fieldHtml=document.createElement("div"),this.fieldHtml.setAttribute("class","bf-fields"),this.fieldHtml.innerHTML=this.getFieldHTML(n.player),n.htmlSelector.appendChild(this.fieldHtml),n.printLog){var l='
'+f.getMessage("filter")+': '+f.getPlayerName(n.player.kUser)+' '+f.getPlayerName(n.player.kBrain)+' '+f.getMessage("full")+"
";this.logFilter=null,this.actFilter="",this.logHtml=document.createElement("div"),this.logHtml.setAttribute("class","bf-logger"),this.logHtml.innerHTML="

"+f.getMessage("info_log_title")+l+"

",n.htmlSelector.appendChild(this.logHtml)}document.getElementById("new_game").onclick=function(){r.battlefield.run()},document.getElementById("config").onclick=function(){r.battlefield.updateConfig()}}var n={},l={printName:!0,printLog:!0,printHelp:!0,fSize:{h:15,v:15},fBarrier:[[5,2],[4,3],[3,4],[2,5],[1,6]],fMarker:{h:"number",v:"char"}},s={player:{kUser:"FUser",kBrain:"FBrain"},winner:{kUser:0,kBrain:0},tPoint:{DEF:0,BAR:1,KIL:2,NUL:3}},o=!1,c="middle";t.prototype.run=function(){try{var e=new r,t=e.setBarrier();new i(t,this)}catch(e){f.showExceptions(e)}},t.prototype.getLevel=function(e){var t={easy:{fSize:{h:10,v:10},fBarrier:[[4,1],[3,2],[2,3],[1,4]]},middle:{fSize:{h:15,v:15},fBarrier:[[5,2],[4,3],[3,4],[2,5],[1,6]]},hard:{fSize:{h:15,v:20},fBarrier:[[6,1],[5,2],[4,3],[3,4],[2,5],[1,6]]}};if("object"==typeof t[e])return t[e];throw new Error(f.getMessage("err_invalid_level"))},t.prototype.setLevel=function(e){var t=this.getLevel(e);return n.fSize=t.fSize,n.fBarrier=t.fBarrier,c=e,this},t.prototype.updateConfig=function(){function e(e){s.querySelector("#opt-fsize").querySelectorAll("label").forEach(function(t){var r=t.querySelector("input");e?(t.classList="",r.disabled=!1):(t.classList="no-act",r.disabled=!0)}),s.querySelector("#opt-fbarrier").querySelectorAll("label").forEach(function(t){var r=t.querySelector("input");e?(t.classList="",r.disabled=!1):(t.classList="no-act",r.disabled=!0)})}function t(){var e={fSize:{h:0,v:0},fBarrier:[]};return e.fSize.h=parseInt(s.querySelector("input#type-h").value),e.fSize.v=parseInt(s.querySelector("input#type-v").value),s.querySelector("#opt-fbarrier").querySelectorAll("label").forEach(function(t){var r=t.querySelector("input"),i=parseInt(r.getAttribute("bar")),a=parseInt(r.value);a>0&&e.fBarrier.push([i,a])}),e}function r(e){s.querySelector("input#type-h").value=e.fSize.h,s.querySelector("input#type-v").value=e.fSize.v,s.querySelector("#opt-fbarrier").querySelectorAll("input").forEach(function(e){e.value=0}),e.fBarrier.forEach(function(e){var t=e[0],r=e[1];s.querySelector("#opt-fbarrier").querySelector("input#bar_"+t).value=r})}var i=this,a='
Уровень сложности:
Размер поля:
Корабли на поле
';a="
"+a+"
";var l=c;f.modalWindow(f.getMessage("options"),a,[{elValue:f.getMessage("new_game"),onClick:function(e){e.close();try{if("user"==l){var r=t();n.fSize=r.fSize,n.fBarrier=r.fBarrier,c=l}else l.length>0&&l!=c&&i.setLevel(l);i.run()}catch(e){f.showExceptions(e)}}},{elValue:f.getMessage("set_default_params"),elClass:"btn-warning",onClick:function(e){e.close(),i.setLevel("middle"),i.run()}},{elValue:f.getMessage("close"),elClass:"btn-danger",onClick:function(e){e.close()}}]);var s=document.querySelector(".bf-config"),o=document.getElementById("config_level").querySelectorAll("button");e("user"==c),r(n),o.forEach(function(t){t.onclick=function(t){return l=t.target.value,o.forEach(function(e){e.classList.remove("active")}),t.target.classList+=" active","user"==l?e(!0):(e(!1),r(i.getLevel(l))),!1}})},r.prototype.setBarrier=function(){function e(r,a){if(!(i>0))throw new RangeError(f.getMessage("err_max_iteration"));i--;for(var l=[],s=f.rand(0,n.fSize.v-1),o=f.rand(0,n.fSize.h-1),c=f.rand(1,2)%2,u=0;us||t<0||t>o)return!1;if(r[e][t]!==n.tPoint.DEF)return!1;for(var c=0;c=0&&a<=s&&l>=0&&l<=o&&r[a][l]!=n.tPoint.DEF)return!1;return!0}var r=this,i=100*this.fName.length;return this.fName.forEach(function(t){var i=r.fields[t];n.fBarrier.forEach(function(t){for(var r=t[0],a=t[1],n=0;n0)l=r();else{var o=f.rand(0,i._lsShot[e].length-1);l=i._lsShot[e][o],i._lsShot[e].splice(o,1)}if(l instanceof Array&&"undefined"!==l[0]&&"undefined"!==l[1]){var c=l[0],u=l[1];if(c>=0&&c=0&&u=0&&h=0&&pc?c:m,y=y=0&&p=0&&v';for(var a=0;a";for(var l=0;l'+c+"":"",o=0===l?'
'+u+"
":""}var d="";t&&(d=i[e][a][l]==n.tPoint.BAR?" let":""),r+=""+s+o+'
'}r+=""}return r+=""}function r(e){var t="";return n.fBarrier.forEach(function(e){for(var r="",i=0;i';t+='
x'+e[1]+" "+r+"
"}),'
'+t+"
"}var i=this.fields,a="";for(var l in e){var s=e[l],o=s==n.player.kUser?"lf":"rg",c="lf"==o;a+='
',a+=t(s,c),a+=r(s),a+="
"}return a},a.prototype.showProgress=function(e){return e?(this.fieldHtml.querySelectorAll("table.field.timeout").forEach(function(e){e.classList.remove("timeout")}),this.fieldHtml.querySelector("table.field#"+e).classList.add("timeout"),n.printName?(this.nameHtml.querySelectorAll(".name").forEach(function(e){e.classList.remove("act")}),void this.nameHtml.querySelector(".name#"+e).classList.add("act")):null):(this.fieldHtml.querySelectorAll("table.field").forEach(function(e){e.classList.add("timeout")}),n.printName&&this.nameHtml.querySelectorAll(".name").forEach(function(e){e.classList.remove("act")}),!1)},a.prototype.clickToField=function(e,t){this.fieldHtml.querySelector("table.field#"+e).onclick=t},a.prototype.setMarker=function(e,t,r,i){var a=e[0],l=e[1],s="";switch(i=i||!1,r){case n.tPoint.NUL:s=i?"auto-null":"null";break;case n.tPoint.KIL:s="kill";break;case n.tPoint.KIL+"_death":s="kill";break;default:return null}i||this.printLog(e,t,r);var o=this.fieldHtml.querySelector("table.field#"+t).rows[a].cells[l];if(o.querySelector(".box").className+=" "+s,!i){var c=document.createElement("div");c.setAttribute("class","shot"),o.appendChild(c),f.animateOpacity(c,2e3)}},a.prototype.setHelpMarker=function(e,t,r){if(!n.printHelp)return!1;for(var i=[[-1,-1],[1,-1],[1,1],[-1,1]],a=e[0],l=e[1],s=0;s=0&&o=0&&c'+p+''+f.getMessage(c)+''+u+''+h+"";var v=document.createElement("li");if(v.setAttribute("id",t),v.setAttribute("data-x",l),v.setAttribute("data-y",s),this.actFilter==t&&v.setAttribute("class","no-active"),v.innerHTML=a,this.logHtml.querySelector("ul").insertBefore(v,this.logHtml.querySelector("ul").firstChild),v.onmouseover=function(e){if("LI"!=e.target.nodeName)return!1;var t=e.target.getAttribute("id"),r=e.target.getAttribute("data-x"),a=e.target.getAttribute("data-y"),n=i.fieldHtml.querySelector(".field#"+t).rows[r].cells[a],l=document.createElement("div");l.setAttribute("class","shot"),n.appendChild(l),v.onmouseout=function(){f.animateOpacity(l,1e3)}},null==this.logFilter){this.logFilter=this.logHtml.querySelector(".bf-filter"),this.logFilter.classList="bf-filter";var m=this.logFilter.querySelector("#full");this.logFilter.querySelectorAll("span").forEach(function(e){e.onclick=function(e){return m!=e.target&&(m&&m.classList.remove("active"),i.actFilter=e.target.getAttribute("id"),m=e.target,m.classList.add("active"),void i.logHtml.querySelectorAll("li").forEach(function(e){e.classList=i.actFilter==e.getAttribute("id")?"no-active":""}))}})}},a.prototype.showShip=function(e){for(var t=this.fields[e],r=this.fieldHtml.querySelector("table#"+e),i=0;i

'+i+"

"+l+"

";f.modalWindow(f.getMessage("game_over"),s,[{elValue:f.getMessage("new_game"),onClick:function(e){e.close(),t.battlefield.run()}},{elValue:f.getMessage("update_options"),elClass:"btn-warning",onClick:function(e){e.close(),t.showProgress(!1),t.battlefield.updateConfig()}},{elValue:f.getMessage("close"),elClass:"btn-danger",onClick:function(e){e.close(),t.showProgress(!1)}}])};var f={playerName:{def:{kUser:"Игрок",kBrain:"Компьютер"}},rand:function(e,t){return e=e||0,t=t||100,parseInt(Math.random()*(t-e+1)+e)},getLetter:function(e,t){var t=t||"",r="ABCDEFGHIJKLMNOPQRSTUVWXYZ";return e>r.length?this.getLetter(e-r.length,""==t?1:t+1):r[e]+t},shuffle:function(e){for(var t,r,i=0;iпопробуешь еще?",info_loser:"В следующий раз повезет больше,
попробуешь еще?"};return"string"==typeof t[e]&&t[e].length>0?t[e]:e},getPlayerName:function(e){var t=e==n.player.kUser?"kUser":"kBrain";return this.playerName.def[t]},showExceptions:function(e){var r="",i="",a=[];switch(e.name){case"Error":r=f.getMessage("info_title_error"),i=e.message,a=[{elValue:f.getMessage("set_default_params"),onClick:function(e){e.close();var r=new t(n.htmlSelector,n);r.setLevel("middle").run()}}];break;case"RangeError":r=f.getMessage("info_title_range_error"),i=e.message,a=[{elValue:f.getMessage("update_options"),onClick:function(e){e.close();var r=new t(n.htmlSelector,n);r.updateConfig()}},{elValue:f.getMessage("set_default_params"),elClass:"btn-warning",onClick:function(e){e.close();var r=new t(n.htmlSelector,n);r.setLevel("middle").run()}}];break;default:r=e.name,i=e.message}this.modalWindow(r,i,a)},modalWindow:function(e,t,r){function i(e,t){return function(r){"function"==typeof t[e].onClick&&t[e].onClick(a,r)}}var a={font:!1,window:!1,construct:function(){if(this.createHTML().autoPosition(),!(r instanceof Array)||0==r.length)return this.window.querySelector(".footer").style.display="none",!1;for(var e=this.window.querySelector(".footer"),t=[],a=0;a
'+e+'
'+t+'
',document.body.appendChild(this.font),document.body.appendChild(this.window),this},autoPosition:function(){var e=this.window.clientWidth/2,t=this.window.clientHeight/2;this.window.style.marginTop=t*-1+"px",this.window.style.marginLeft=e*-1+"px"},close:function(){document.body.removeChild(this.font),document.body.removeChild(this.window)}};a.construct()},animateOpacity:function(e,t){e.style.opacity=1;var r=setInterval(function(){if(e.style.opacity=e.style.opacity-100/(t/.1),e.style.opacity<=0){clearInterval(r);try{e.parentNode.removeChild(e)}catch(e){}}},1)}};e.Battlefield=t}(window),function(e,t,r){(t[r]=t[r]||[]).push(function(){try{t.yaCounter42568409=new Ya.Metrika({id:42568409,clickmap:!0,trackLinks:!0,accurateTrackBounce:!0})}catch(e){}});var i=e.getElementsByTagName("script")[0],a=e.createElement("script"),n=function(){i.parentNode.insertBefore(a,i)};a.type="text/javascript",a.async=!0,a.src="https://mc.yandex.ru/metrika/watch.js","[object Opera]"==t.opera?e.addEventListener("DOMContentLoaded",n,!1):n()}(document,window,"yandex_metrika_callbacks"); -------------------------------------------------------------------------------- /src/css/battlefield.helper.css: -------------------------------------------------------------------------------- 1 | .bf-go-smile { 2 | float: left; 3 | width: 100px; 4 | height: 100px; 5 | margin-left: 90px; 6 | } 7 | 8 | .bf-go-smile.winner { 9 | background-image: url(data:image/png;base64,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); 10 | } 11 | 12 | .bf-go-smile.lose { 13 | background-image: url(data:image/png;base64,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); 14 | } 15 | 16 | .bf-go-text { 17 | float: left; 18 | margin: 0 0 0 35px; 19 | padding: 15px 0; 20 | } 21 | 22 | .bf-go-text h2 { 23 | margin-bottom: 10px 24 | } 25 | 26 | table.bf-config { 27 | width: 100%; 28 | border-collapse: collapse; 29 | } 30 | 31 | table.bf-config td { 32 | padding: 10px 0 33 | } 34 | 35 | table.bf-config td.name { 36 | width: 28%; 37 | font-size: 13px; 38 | font-weight: 700; 39 | vertical-align: top 40 | } 41 | 42 | table.bf-config td.name.top { 43 | padding: 15px 0 44 | } 45 | 46 | table.bf-config td.options { 47 | width: 73% 48 | } 49 | 50 | table.bf-config td .border { 51 | border-top: 1px solid rgba(34, 36, 38, .15) 52 | } 53 | 54 | table.bf-config td.options .btn { 55 | padding: 4px 14px; 56 | border-radius: 20px; 57 | background: #fff; 58 | border: 1px solid rgba(34, 36, 38, .15); 59 | color: rgba(0, 0, 0, .8); 60 | } 61 | 62 | table.bf-config td.options .btn:hover { 63 | background-color: #F9FAFB; 64 | } 65 | 66 | table.bf-config td.options .btn.active { 67 | background-color: #e7ff7f !important; 68 | } 69 | 70 | table.bf-config td.options .btn.rg { 71 | float: right; 72 | margin: 0; 73 | color: #794B02; 74 | background-color: #ffeacc; 75 | } 76 | 77 | table.bf-config td.options label { 78 | display: inline-block; 79 | margin: 2px 25px 2px 0; 80 | cursor: pointer 81 | } 82 | 83 | table.bf-config td.options label input[type=number] { 84 | color: rgba(0, 0, 0, .8); 85 | width: 45px; 86 | font-size: 13px; 87 | border: 1px solid rgba(34, 36, 38, .15); 88 | margin: -3px 0 0 0; 89 | padding: 3px 4px; 90 | border-radius: 3px; 91 | background-color: rgba(255, 249, 0, 0.25); 92 | } 93 | 94 | table.bf-config td.options label.no-act input[type=number] { 95 | border-color: #fff; 96 | background: none 97 | } 98 | 99 | table.bf-config td.options input[type=checkbox], table.bf-config td.options input[type=radio] { 100 | float: left; 101 | margin-top: 2px; 102 | width: auto; 103 | margin-right: 5px; 104 | } 105 | 106 | table.bf-config td.options.no-label label { 107 | cursor: default !important; 108 | } 109 | 110 | table.bf-config td.options div { 111 | padding-bottom: 4px 112 | } 113 | 114 | table.bf-config td.options div.top { 115 | padding-top: 10px 116 | } -------------------------------------------------------------------------------- /dist/battlefield.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * battlefield-js v1.0.1 3 | * This is classic game Battlefield for browsers, implemented on language JavaScript. 4 | * 5 | * repository: git+https://github.com/alx2das/battlefield-js.git 6 | * bugs: git+https://github.com/alx2das/battlefield-js.git 7 | * homepage: https://alx2das.github.io/battlefield-js/examples/ 8 | * 9 | * Copyright 2017, Alexandr Builov 10 | * Date: Sun Feb 05 2017 11 | */ 12 | .container,body,html{min-height:100%}.bf-fields{background-color:#7a8b9f}.bf-fields .board{width:50%}.bf-fields .board.lf{float:left}.bf-fields .board.rg{float:right}.bf-fields table.field{border-collapse:collapse;margin:25px}.bf-fields table.field td,.bf-fields table.field td .box{border:2px solid #E5EAFF}.bf-fields table.field td .box,.bf-fields table.field td .ll,.bf-fields table.field td .mm{width:28px;height:28px;line-height:28px;text-align:center}.bf-fields table.field td .ll,.bf-fields table.field td .mm{position:absolute;z-index:1;font-family:Georgia;color:#afafaf;font-weight:700;font-size:12px}.bf-fields table.field td .ll{left:-28px}.bf-fields table.field td .mm{top:-28px}.bf-fields table.field td{position:relative}.bf-fields table.field td .box{float:left;margin:-2px}.bf-fields .list-let .box.kill,.bf-fields table.field td .box.auto-null,.bf-fields table.field td .box.kill,.bf-fields table.field td .box.null{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAAcCAYAAAA0u3w+AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyFpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NDkxMSwgMjAxMy8xMC8yOS0xMTo0NzoxNiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpDRjMwN0VCOEM4NDcxMUU1OTM5M0RBNkE1MDVDQUVEMSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpDRjMwN0VCOUM4NDcxMUU1OTM5M0RBNkE1MDVDQUVEMSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkNGMUMzMzI1Qzg0NzExRTU5MzkzREE2QTUwNUNBRUQxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkNGMUMzMzI2Qzg0NzExRTU5MzkzREE2QTUwNUNBRUQxIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+aamjFgAAAmdJREFUeNrs2E1IFGEcx/FdldlVWosiyg7WKQo6LNFFykgoL4GKEER16OUQ5kW6RZ3yUKcugVJLJEKIHSSiRKUoO0UvaChSInSIXpBa1y13Y3fW6fvIXxi2nX1zwJ1lHviwLzPzzPyet9lZr2EYnnIuFZ4yL25AN2Cxxeu10oKNWbZn5qAe/Ixh1ObYr8apQ3QKAQkZsNhnK1qdPAcHcVBCbkjrMfU6hJdODjgAdYM+JCG3oBeVsu0bvudTUVUhZz3cdHT17THsxhtMI/7qxTO7wql6w3iNBjRiTi1LuI0WNOVbWVWBJ1cteAPNSOEi7qMPCwXWFcRxLEqYD0hiGRPYbtp3k7x2YCbf4VlMwHbVkSdPtCd9Pl8wkUhMj449Px+ORL7y/cMC65qUBeW0zLk6CabC3sU1i+N6TQvNDmmUGbvmoFrVlgm3XzWOpmlBf031XzlJMUWNgn7sQRfqcRnXoVkccwV/cE8a5aOdi8y4ajld12dXri6VmossRDbLpF9LSUqv7ZUezFZUr92UuTgiQ9q2gEu482R41PPu/cT4o8dPPbF4PMR3v2xYXNScDMk8z1W6ZXXdafcc/IEHi9HoLHS1euKLrHJrLZNy4YN5/BA4g21yr/wpDZ+5qOfBdSE3OpMLWDLUJeXW/9/xFucphYC1GEgLEEU4Q7C3pvddTgh4AJ8whZD04j5UoE+CxHAVOjR04jeSOFLKAf1oRCDDcFV6JOA5+dxh2rYLI5hHfbaA3nX7TybLM5yUW/DjklUNOItTK08WhhEr9UUmXasMyVz71aGt9HrQ/U/GDegGdAOWQ/knwAALfSfZ0tNOqgAAAABJRU5ErkJggg==) no-repeat}.bf-fields table.field td .box.let{background-color:#E5EAFF}.bf-fields table.field td .box.null{opacity:1!important}.bf-fields table.field td .box.auto-null{opacity:.5}.bf-fields table.field td .box.kill{background-position:-28px 0;background-color:#a1b9d6}.bf-fields table.field td .box.death{background-color:#deabab}.bf-fields .shot{position:absolute;z-index:1;width:28px;height:28px;margin:-2px;background-color:rgba(255,249,0,.7);border:2px solid #fff900}.bf-fields .list-let{margin:25px 15px 0;float:left}.bf-fields .list-let .let{margin-bottom:10px}.bf-fields .list-let .let:after{content:" ";display:block;clear:both}.bf-fields .list-let span{width:26px;height:26px;line-height:26px;margin:0 10px;text-align:center;color:#afafaf;font-weight:700;display:inline-block;background:0 0;border:none}.bf-fields:after,.bf-logger:after,.bf-player-name:after,.ctn-footer:after,.ctn-header:after{display:block;content:" ";clear:both}.bf-fields .list-let span .shot{width:24px;height:24px;margin:-27px 0 0}.bf-fields .list-let .box{float:left;width:24px;height:24px;margin-left:-2px;background-color:rgba(165,188,208,.5)!important;border:2px solid #E5EAFF}.ctn-footer .file a:hover,.ctn-header{border-bottom:1px solid #7a8b9f}.bf-fields .list-let .box.kill{background-position:-28px 0}.bf-fields .board.rg table.field td .box{cursor:pointer}.bf-fields .board.rg table.field td .box:hover{box-shadow:inset 0 0 5px 2px #ff8d8d}.bf-fields .board.rg table.field td .box.auto-null:hover,.bf-fields .board.rg table.field td .box.kill:hover,.bf-fields .board.rg table.field td .box.let:hover,.bf-fields .board.rg table.field td .box.null:hover{box-shadow:none!important;cursor:default!important}.bf-fields .board table.field.timeout{opacity:.7;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.bf-fields .board table.field.timeout .box{cursor:wait!important}.bf-fields .board.lf .list-let,.bf-fields .board.lf .list-let .box,.bf-fields .board.lf table.field,.bf-fields .board.rg .list-let span{float:right}.bf-fields .board.lf .list-let span,.bf-fields .board.rg .list-let,.bf-fields .board.rg .list-let .box,.bf-fields .board.rg table.field{float:left}*{margin:0;padding:0}body,html{height:100%;font-family:Arial,sans-serif}.wrap{width:950px;margin:0 auto}.container .clear-footer{height:46px}.ctn-header{padding:20px 0}.ctn-header .logotype{float:left;color:#7a8b9f;font-weight:700;font-size:26px;text-decoration:none}.ctn-header .logotype small{font-weight:400;font-size:12px;color:#b5b5b5}.ctn-header .soc-btn{float:right;padding:3px 0}.btn,.ctn-footer .copy{float:left}.ctn-footer{padding:15px 0;margin-top:-46px;border-top:1px solid #7a8b9f;font-size:13px;color:#b5b5b5}.ctn-footer .file{float:left;margin-left:25px}.ctn-footer .file a{color:#7a8b9f;text-decoration:none}.bf-fields,.bf-logger,.bf-modal-window,.bf-player-name,.btn{font-family:Arial,sans-serif;color:#7a8b9f;font-size:13px}.bf-fields,.bf-logger,.bf-player-name{padding:25px 0}.btn{margin-right:10px;outline:0;margin-bottom:0;font-weight:400;text-align:center;vertical-align:middle;cursor:pointer;border:1px solid transparent;white-space:nowrap;padding:7px 14px;font-size:13px;line-height:1.2;border-radius:3px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#5cb85c;color:#fff}.btn:active,.btn:hover{background-color:#449d44}.btn.btn-warning{background-color:#f0ad4e}.btn.btn-warning:active,.btn.btn-warning:hover{background-color:#ec971f}.btn.btn-danger{background-color:#d9534f;float:right;margin-right:0}.btn.btn-danger:active,.btn.btn-danger:hover{background-color:#c9302c}.btn.btn-danger:active{background-image:none}span.js{margin-left:15px;cursor:pointer;color:#7a8b9f;border-bottom:1px dashed #7a8b9f}span.js:hover{border-bottom:none}.bf-go-smile{float:left;width:100px;height:100px;margin-left:90px}.bf-go-smile.winner{background-image:url(data:image/png;base64,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)}.bf-go-smile.lose{background-image:url(data:image/png;base64,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)}.bf-go-text{float:left;margin:0 0 0 35px;padding:15px 0}.bf-go-text h2{margin-bottom:10px}table.bf-config{width:100%;border-collapse:collapse}table.bf-config td{padding:10px 0}table.bf-config td.name{width:28%;font-size:13px;font-weight:700;vertical-align:top}table.bf-config td.name.top{padding:15px 0}table.bf-config td.options{width:73%}table.bf-config td .border{border-top:1px solid rgba(34,36,38,.15)}table.bf-config td.options .btn{padding:4px 14px;border-radius:20px;background:#fff;border:1px solid rgba(34,36,38,.15);color:rgba(0,0,0,.8)}table.bf-config td.options .btn:hover{background-color:#F9FAFB}table.bf-config td.options .btn.active{background-color:#e7ff7f!important}table.bf-config td.options .btn.rg{float:right;margin:0;color:#794B02;background-color:#ffeacc}table.bf-config td.options label{display:inline-block;margin:2px 25px 2px 0;cursor:pointer}table.bf-config td.options label input[type=number]{color:rgba(0,0,0,.8);width:45px;font-size:13px;border:1px solid rgba(34,36,38,.15);margin:-3px 0 0;padding:3px 4px;border-radius:3px;background-color:rgba(255,249,0,.25)}table.bf-config td.options label.no-act input[type=number]{border-color:#fff;background:0 0}table.bf-config td.options input[type=checkbox],table.bf-config td.options input[type=radio]{float:left;margin-top:2px;width:auto;margin-right:5px}table.bf-config td.options.no-label label{cursor:default!important}table.bf-config td.options div{padding-bottom:4px}table.bf-config td.options div.top{padding-top:10px}.bf-logger{max-width:950px;margin:0 auto}.bf-logger h2{padding-bottom:15px}.bf-logger ul{list-style:none;max-height:300px;overflow-y:auto;margin-bottom:25px}.bf-logger ul li{display:block;padding:7px 0;border-bottom:1px solid #d6d6d6}.bf-logger ul li.no-active{display:none}.bf-logger ul li:hover{background-color:#f6f6f6}.game-log ul li:last-child{border-bottom:none}.bf-logger ul li .point,.bf-logger ul li .time,.bf-logger ul li .type{width:60px;display:inline-block}.bf-logger ul li .point,.bf-logger ul li .time{font-weight:700}.bf-logger ul li .type{margin:0 10px;font-size:12px;padding:2px 0;text-align:center;border-radius:2px;font-weight:700}.bf-logger ul li .type.kil{background-color:#d9534f;color:#fff}.bf-logger ul li .type.war{background-color:#f0ad4e;color:#fff}.bf-logger ul li .name{font-size:14px}.bf-logger ul li .time{float:right}.bf-logger .bf-filter{float:right;font-weight:400;font-size:13px}.bf-logger .bf-filter.no-active{display:none}.bf-logger .bf-filter span.active{border-bottom:1px solid}.bf-modal-font{position:fixed;z-index:9;top:0;left:0;width:100%;height:100%;background:rgba(122,139,159,.8)}.bf-modal-window{position:fixed;z-index:19;top:50%;left:50%;width:600px;background:#fff;border-radius:4px}.bf-modal-window .content{padding:25px 15px 15px}.bf-modal-window .content span{float:right;font-size:12px;font-weight:400;padding:2px 0;color:#7A8BB4;font-style:italic}.bf-modal-window .header{font-weight:700;font-size:16px;padding-bottom:16px;border-bottom:2px solid rgba(122,139,159,.3)}.bf-modal-window .cont{padding:30px 0}.bf-modal-window .footer{padding-top:15px;border-top:2px solid rgba(122,139,159,.3)}.bf-modal-window .cont:after,.bf-modal-window .footer:after,.bf-modal-window .header:after{content:" ";clear:both;display:block}.bf-player-name{text-align:center;width:950px;margin:0 auto}.bf-player-name span{display:inline-block;padding:0 10px;font-size:15px;height:24px;line-height:24px}.bf-player-name .name{width:120px;background:#7a8b9f;color:#fff;margin:0 10px 0 6px;padding:4px 10px;border-radius:3px;height:auto;line-height:1.1}.bf-player-name .name.act{background:#f0ad4e}.bf-player-name .total{min-width:30px;text-align:center;color:#7a8b9f;font-weight:700}.bf-player-name .opt{float:left}.bf-player-name .left{float:left;margin-top:-5px}.bf-player-name .center{width:460px;margin:0 auto}.bf-player-name .center:after{content:" ";clear:both;display:block}.bf-player-name .right{float:right}.bf-player-name .left a{color:#7a8b9f;font-weight:700;font-size:26px;text-decoration:none}.bf-player-name .right span{padding:0} --------------------------------------------------------------------------------