мж.ресурсы = [ [БАЗА + "модели/фишка/2019-09-08.osgt", "mod"], [БАЗА + "текстуры/заглушка.png", "tex.stub"], [БАЗА + "шейдеры/освещение-изображение.vert", "ver"], [БАЗА + "шейдеры/освещение-изображение.frag", "fra"], [БАЗА + "раскладки/X_shaped.layout", "lay"], /* [БАЗА + "классы/Раскладка/2019-08-16.js", "Layout"], [БАЗА + "классы/Фишка/2019-09-07.js", "Tile"], [БАЗА + "темыФишек/заглушка/2019-09-08.js", "theme.stub"], */ ]; мж.начали = 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() { //eval(муром.ресурсы.ресурс("Layout").содержимое); var содержимое = муром.ресурсы.ресурс("lay").содержимое; мж.раскладка = new Раскладка(); мж.раскладка.разобрать(содержимое); }; мж.фишки = []; мж.создатьФишки = function() { //eval(муром.ресурсы.ресурс("Tile").содержимое); 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(муром.ресурсы.ресурс("theme.stub").contents); 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]; }); };