|
-
- // Фишка.
-
- function Фишка()
- {
- this.позиция = null;
- this.узел = null;
- this.группа = null;
-
- this.нейтраль = null;
- this.выбор = null;
- }
- Фишка.prototype.показатьВыбор = function()
- {
- this.узел.задатьМатериал(this.выбор);
- }
- Фишка.prototype.показатьНейтраль = function()
- {
- this.узел.задатьМатериал(this.нейтраль);
- }
- Фишка.prototype.показать = function()
- {
- this.узел.задатьМаску(0x0);
- }
- Фишка.prototype.скрыть = function()
- {
- // Специальная маска OpenSceneGraph для скрытия узла от камеры.
- this.узел.задатьМаску(0xFFFFFFFF);
- }
- Object.defineProperty(Фишка.prototype, 'имя', {
- get: function()
- {
- return this.узел.имя;
- }
- });
-
- // Раскладка.
-
- function Раскладка()
- {
- this.версия = "";
- this.ширина = 0;
- this.высота = 0;
- this.глубина = 0;
- this.позиции = [];
-
- // Для внутреннего пользования.
- this.поля = [];
- };
- Раскладка.prototype.разобратьСлужебнуюИнформацию = function(содержимое)
- {
- const ключи = {
- "версия": "kmahjongg-layout-v",
- "комментарий": "#",
- "ширина": "w",
- "высота": "h",
- "глубина": "d"
- };
-
- // ВНИМАНИЕ Версия 1.0 предполагает заданные заранее ширину и высоту.
- this.версия = "";
- this.глубина = 0;
- this.ширина = 32;
- this.высота = 16;
- this.поля = [];
-
- var поле = [];
- var строки = содержимое.split("\n");
- for (var номер in строки)
- {
- var строка = строки[номер].trim();
-
- // Пропуск.
- if (строка.startsWith(ключи.комментарий))
- {
- continue;
- }
-
- // Служебная информация.
- if (строка.startsWith(ключи.версия))
- {
- this.версия = строка.split(ключи.версия)[1];
- }
- else if (строка.startsWith(ключи.ширина))
- {
- this.ширина = строка.split(ключи.ширина)[1];
- }
- else if (строка.startsWith(ключи.высота))
- {
- this.высота = строка.split(ключи.высота)[1];
- }
- else if (строка.startsWith(ключи.глубина))
- {
- this.глубина = строка.split(ключи.глубина)[1];
- }
- // Поле.
- else
- {
- поле.push(строка);
- if (поле.length >= this.высота)
- {
- this.поля.push(поле.slice());
- поле = [];
- }
- }
- }
-
- if (this.глубина == 0)
- {
- this.глубина = this.поля.length;
- }
- }
- Раскладка.prototype.разобратьПозиции = function()
- {
- this.позиции = [];
- for (var номер in this.поля)
- {
- var поле = this.поля[номер];
- for (var строка = 0; строка < this.высота - 1; ++строка)
- {
- for (var столбец = 0; столбец < this.ширина - 1; ++столбец)
- {
- if (
- поле[строка][столбец] == "1" &&
- поле[строка][столбец + 1] == "2" &&
- поле[строка + 1][столбец] == "4" &&
- поле[строка + 1][столбец + 1] == "3"
- ) {
- this.позиции.push([номер, строка, столбец]);
- }
- }
- }
- }
- }
- Раскладка.prototype.разобрать = function(содержимое)
- {
- this.разобратьСлужебнуюИнформацию(содержимое);
- this.разобратьПозиции();
- }
- Раскладка.prototype.отладка = function()
- {
- var о = "";
- о += "Отладочная информация о раскладке:\n";
- о += " версия: '" + this.версия + "'\n";
- о += " ширина: '" + this.ширина + "'\n";
- о += " высота: '" + this.высота + "'\n";
- о += " глубина: '" + this.глубина + "'\n";
- о += " позиции:\n";
- for (var номер in this.позиции)
- {
- var п = this.позиции[номер];
- о += " п(" + номер + "): '" + п[0] + ", " + п[1] + ", " + п[2] + "'\n";
- }
- return о;
- }
-
- // Игра.
-
- мж.ресурсы = [
- [Б + "модели/фишка/2019-09-08.osgt", "mod"],
- [Б + "текстуры/заглушка.png", "tex.stub"],
- [Б + "шейдеры/освещение-изображение.vert", "ver"],
- [Б + "шейдеры/освещение-изображение.frag", "fra"],
- [Б + "раскладки/X_shaped.layout", "lay"],
- ];
-
- мж.начали = new Уведомитель();
- мж.начать = function()
- {
- муром.камера.позиция = [0, -70, 0];
- муром.камера.вращение = [90, 0, 0];
- мж.сцена = муром.узлы.создатьСферу("sce", 0);
- мж.сцена.вращение = [60, 0, 0];
- var мир = муром.узлы.узел("mir");
- мир.добавитьДитя(мж.сцена);
-
- мж.начали.уведомить();
- };
-
- мж.задатьНейтральныйМатериал = function()
- {
- var мат = муром.материалы.создатьМатериал("N");
- var изо = муром.ресурсы.ресурс("tex.stub");
- мат.задатьТекстуру("image", изо);
- var вер = муром.ресурсы.ресурс("ver").содержимое;
- var фра = муром.ресурсы.ресурс("fra").содержимое;
- мат.задатьШейдеры(вер, фра);
- мж.сцена.задатьМатериал(мат);
- };
-
- мж.разобратьРаскладку = function()
- {
- var содержимое = муром.ресурсы.ресурс("lay").содержимое;
- мж.раскладка = new Раскладка();
- мж.раскладка.разобрать(содержимое);
- };
-
- мж.фишки = [];
- мж.создатьФишки = function()
- {
- for (var номер in мж.раскладка.позиции)
- {
- var ф = new Фишка();
- ф.позиция = мж.раскладка.позиции[номер];
- мж.фишки.push(ф);
- }
- };
-
- мж.размерФишки = {
- "ширина": 2.0,
- "высота": 3.0,
- "глубина": 1.0,
- };
- мж.создатьУзлыФишек = function()
- {
- const шагФишки = 2.0;
- const коэффициенты = {
- "x": мж.размерФишки.ширина / шагФишки,
- "y": -мж.размерФишки.высота / шагФишки,
- "z": мж.размерФишки.глубина,
- };
- var мод = муром.ресурсы.ресурс("mod");
-
- for (var номер in мж.фишки)
- {
- var имя = номер.toString();
- var узел = муром.узлы.создатьУзел(имя, мод);
- мж.сцена.добавитьДитя(узел);
-
- var ф = мж.фишки[номер];
- var п = ф.позиция;
- узел.позиция = [
- п[2] * коэффициенты.x,
- п[1] * коэффициенты.y,
- п[0] * коэффициенты.z,
- ];
- ф.узел = узел;
- }
- };
-
- мж.центрироватьСцену = function()
- {
- var границы = {
- "лево": 1000,
- "право": -1000,
- "верх": -1000,
- "низ": 1000,
- };
- for (var номер in мж.фишки)
- {
- const ф = мж.фишки[номер];
- const x = ф.узел.позиция[0];
- const y = ф.узел.позиция[1];
- if (x < границы.лево)
- {
- границы.лево = x;
- }
- if (x > границы.право)
- {
- границы.право = x;
- }
- if (y < границы.низ)
- {
- границы.низ = y;
- }
- if (y > границы.верх)
- {
- границы.верх = y;
- }
- }
-
- const ширина = границы.право - границы.лево + мж.размерФишки.ширина;
- const высота = границы.верх - границы.низ + мж.размерФишки.высота;
- мж.сцена.позиция = [-ширина / 2.0, 0, высота / 2.0];
- };
-
- // http://www.rubl.com/rules/mahjong-solitaire-rules.html
- мж.задатьФишкамГруппыПоследовательно = function()
- {
- var группы = [];
- // Генерируем группы.
- const группВсего = 42;
- const группПо4Дубля = 34;
- for (var г = 0; г < группВсего; ++г)
- {
- const четыреДубля = (г < группПо4Дубля);
- const колвоДублей = четыреДубля ? 4 : 1;
- for (var д = 0; д < колвоДублей; ++д)
- {
- группы.push(г);
- }
- }
- // Задаём.
- for (var номер in мж.фишки)
- {
- var ф = мж.фишки[номер];
- ф.группа = Number(группы[номер]);
- }
- };
-
- мж.применитьТемуФишек = function()
- {
- eval(dl["тема.заглушка"]);
- const R = RR();
-
- // Создать текстуры.
- var обозначения = [];
- var текстуры = [];
- for (var номер in R)
- {
- var обозначение = R[номер][0];
- обозначения.push(обозначение);
- var имя = "tile.tex/" + обозначение;
- var содержимое = R[номер][1];
- var текстура = муром.ресурсы.создатьРесурс(имя, содержимое);
- текстуры.push(текстура);
- }
-
- // Создать материалы.
- var нейтраль = [];
- var выбор = [];
- var вер = муром.ресурсы.ресурс("ver").содержимое;
- var фра = муром.ресурсы.ресурс("fra").содержимое;
- // Нейтраль.
- for (var номер = 0; номер < 42; ++номер)
- {
- var имя = "tile.mat/" + обозначения[номер];
- var изо = текстуры[номер];
- var мат = муром.материалы.создатьМатериал(имя);
- мат.задатьТекстуру("image", изо);
- мат.задатьШейдеры(вер, фра);
- нейтраль.push(мат);
- }
- // Выбор.
- for (var номер = 42; номер < 83; ++номер)
- {
- var имя = "tile.mat/" + обозначения[номер];
- var изо = текстуры[номер];
- var мат = муром.материалы.создатьМатериал(имя);
- мат.задатьТекстуру("image", изо);
- мат.задатьШейдеры(вер, фра);
- выбор.push(мат);
- }
-
- // Применить тему.
- for (var номер in мж.фишки)
- {
- var ф = мж.фишки[номер];
- ф.нейтраль = нейтраль[ф.группа];
- ф.выбор = выбор[ф.группа];
-
- ф.показатьНейтраль();
- }
- };
-
- мж.маскаВыбора = 0x2;
- мж.сделатьФишкиВыбираемыми = function()
- {
- for (var номер in мж.фишки)
- {
- var ф = мж.фишки[номер];
- ф.узел.задатьМаску(мж.маскаВыбора);
- }
- }
-
- мж.индексПозиции = function(п)
- {
- return п[0] * 1000000 + п[1] * 1000 + п[2];
- };
-
- мж.индексПозиций = {};
- мж.проиндексироватьПозиции = function()
- {
- for (var номер in мж.фишки)
- {
- var ф = мж.фишки[номер];
- var и = мж.индексПозиции(ф.позиция);
- мж.индексПозиций[и] = true;
- }
- };
-
- мж.естьСоседи = function(позиция, смещениеПоля, смещениеСтолбца)
- {
- for (var смещениеРяда = -1; смещениеРяда <= 1; ++смещениеРяда)
- {
- var сосед = [
- Number(позиция[0]) + смещениеПоля,
- Number(позиция[1]) + смещениеРяда,
- Number(позиция[2]) + смещениеСтолбца,
- ];
- var и = мж.индексПозиции(сосед);
- if (и in мж.индексПозиций)
- {
- return true;
- }
- }
-
- // Соседей нет.
- return false;
- };
-
- мж.можноВыбратьФишку = function(ф)
- {
- // Проверяем наличие фишек с левой и правой сторон одновременно.
- var слева = this.естьСоседи(ф.позиция, 0, -2);
- var справа = this.естьСоседи(ф.позиция, 0, 2);
- if (слева && справа)
- {
- return false;
- }
-
- // Проверяем наличие фишек непосредственно сверху.
- for (var смещение = -1; смещение <= 1; ++смещение)
- {
- if (this.естьСоседи(ф.позиция, 1, смещение))
- {
- return false;
- }
- }
-
- // Фишка не заблокирована.
- return true;
- };
-
- мж.номерВыбраннойФишки = null;
- мж.выбраннаяФишка = null;
- мж.выбранаФишка = new Уведомитель();
- мж.выбратьФишку = function()
- {
- // Определить щелчок.
- if (муром.мышь.нажатыеКнопки.length == 1)
- {
- var узел =
- муром.камера.узелВПозиции(
- муром.мышь.позиция,
- мж.маскаВыбора
- );
- if (узел)
- {
- var номер = Number(узел.имя);
- var ф = мж.фишки[номер];
- if (мж.можноВыбратьФишку(ф))
- {
- мж.номерВыбраннойФишки = номер;
- мж.выбраннаяФишка = ф;
- мж.выбранаФишка.уведомить();
- }
- }
- }
- };
-
- мж.отладитьВыборФишки = function()
- {
- мж.выбранаФишка.подписать(function(){
- console.log(
- "Выбор. Номер: '" +
- мж.номерВыбраннойФишки +
- "' Группа: '" +
- мж.выбраннаяФишка.группа +
- "'"
- );
- });
- };
-
- мж.показатьВыбраннуюФишку = function()
- {
- мж.выбраннаяФишка.показатьВыбор();
- };
-
- мж.выбранныеФишки = [];
- мж.фишкиРазличаются = new Уведомитель();
- мж.фишкиСовпадают = new Уведомитель();
- мж.фишкиСравнили = new Уведомитель();
- мж.сравнитьВыбранныеФишки = function()
- {
- // Собираем.
- мж.выбранныеФишки.push(мж.выбраннаяФишка);
- // Удостоверяемся в наличии пары фишек.
- if (мж.выбранныеФишки.length != 2)
- {
- return;
- }
- var ф1 = мж.выбранныеФишки[0];
- var ф2 = мж.выбранныеФишки[1];
- // Убираем дубликат при двойном выборе одной фишки.
- if (ф1.имя == ф2.имя)
- {
- мж.выбранныеФишки.shift();
- return;
- }
- // Сравниваем.
- if (ф1.группа == ф2.группа)
- {
- мж.фишкиСовпадают.уведомить();
- }
- else
- {
- мж.фишкиРазличаются.уведомить();
- }
- мж.фишкиСравнили.уведомить();
- };
-
- мж.отладитьСравнениеФишек = function()
- {
- мж.фишкиСовпадают.подписать(function(){
- console.log("Фишки совпадают");
- });
- мж.фишкиРазличаются.подписать(function(){
- console.log("Фишки различаются");
- });
- };
-
- мж.скрытьСовпадающиеФишки = function()
- {
- мж.выбранныеФишки[0].скрыть();
- мж.выбранныеФишки[1].скрыть();
- };
-
- мж.убратьСовпадающиеФишки = function()
- {
- var ф1 = мж.выбранныеФишки[0];
- var ф2 = мж.выбранныеФишки[1];
- var и1 = мж.индексПозиции(ф1.позиция);
- var и2 = мж.индексПозиции(ф2.позиция);
- delete мж.индексПозиций[и1];
- delete мж.индексПозиций[и2];
- };
-
- мж.очиститьОтображениеВыбора = function()
- {
- мж.выбранныеФишки[0].показатьНейтраль();
- мж.выбранныеФишки[1].показатьНейтраль();
- };
-
- мж.очиститьВыбор = function()
- {
- мж.номерВыбраннойФишки = 0;
- мж.выбраннаяФишка = null;
- мж.выбранныеФишки = [];
- };
-
- мж.оставшиесяФишки = [];
- мж.задатьОставшиесяФишки = function()
- {
- мж.оставшиесяФишки = мж.фишки.slice();
- };
-
- мж.обновилиОставшиесяФишки = new Уведомитель();
- мж.обновитьОставшиесяФишки = function()
- {
- var и1 = мж.оставшиесяФишки.indexOf(мж.выбранныеФишки[0]);
- мж.оставшиесяФишки.splice(и1, 1);
- var и2 = мж.оставшиесяФишки.indexOf(мж.выбранныеФишки[1]);
- мж.оставшиесяФишки.splice(и2, 1);
-
- мж.обновилиОставшиесяФишки.уведомить();
- };
-
- мж.доступныеДляВыбораФишки = function()
- {
- var фишки = [];
- for (var номер in мж.оставшиесяФишки)
- {
- var ф = мж.оставшиесяФишки[номер];
- if (мж.можноВыбратьФишку(ф))
- {
- фишки.push(ф);
- }
- }
-
- return фишки;
- };
-
- мж.естьХод = function()
- {
- var фишки = мж.доступныеДляВыбораФишки();
- for (var н1 in фишки)
- {
- for (var н2 in фишки)
- {
- var ф1 = фишки[н1];
- var ф2 = фишки[н2];
- if (
- (ф1.имя != ф2.имя) &&
- (ф1.группа == ф2.группа)
- ) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- мж.победа = new Уведомитель();
- мж.поражение = new Уведомитель();
- мж.проверитьЗавершение = function()
- {
- var кончилисьФишки = (мж.оставшиесяФишки.length == 0);
- if (кончилисьФишки)
- {
- мж.победа.уведомить();
- return;
- }
-
- if (!мж.естьХод())
- {
- мж.поражение.уведомить();
- }
- };
-
- мж.отладитьЗавершение = function()
- {
- мж.победа.подписать(function(){
- console.log("Игра завершена. ПОБЕДА!");
- });
- мж.поражение.подписать(function(){
- console.log("Игра завершена. ПОРАЖЕНИЕ :(");
- });
- };
-
- мж.задатьОтображениеЗавершения = function()
- {
- мж.победа.подписать(function(){
- муром.камера.цветОчистки = [0.2, 0.5, 0.2];
- });
- мж.поражение.подписать(function(){
- муром.камера.цветОчистки = [0.5, 0.2, 0.2];
- });
- };
-
- // Игра.
-
- мж.начали.подписатьМного([
- мж.задатьНейтральныйМатериал,
- мж.разобратьРаскладку,
- мж.создатьФишки,
- мж.создатьУзлыФишек,
- мж.центрироватьСцену,
- мж.задатьФишкамГруппыПоследовательно,
- мж.применитьТемуФишек,
- мж.сделатьФишкиВыбираемыми,
- мж.отладитьВыборФишки,
- мж.проиндексироватьПозиции,
- мж.отладитьСравнениеФишек,
- мж.задатьОставшиесяФишки,
- мж.отладитьЗавершение,
- мж.задатьОтображениеЗавершения,
- ]);
- муром.мышь.нажатыеКнопкиИзменились.подписать(мж.выбратьФишку);
- мж.выбранаФишка.подписатьМного([
- мж.показатьВыбраннуюФишку,
- мж.сравнитьВыбранныеФишки,
- ]);
- мж.фишкиСовпадают.подписатьМного([
- мж.убратьСовпадающиеФишки,
- мж.обновитьОставшиесяФишки,
- function(){
- setTimeout(мж.скрытьСовпадающиеФишки, 200);
- },
- ]);
- мж.фишкиРазличаются.подписать(function(){
- setTimeout(мж.очиститьОтображениеВыбора, 200);
- });
- мж.фишкиСравнили.подписать(function(){
- setTimeout(мж.очиститьВыбор, 200);
- });
- мж.обновилиОставшиесяФишки.подписать(мж.проверитьЗавершение);
- // Начать после загрузки ресурсов.
- муром.ресурсы.получить(мж.ресурсы, мж.начать);
|