Игра Маджонг | Mahjong game
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

521 line
18KB

  1. // Ввести зависимости.
  2. eval(dl["апи"]);
  3. eval(dl["Раскладка"]);
  4. eval(dl["Фишка"]);
  5. eval(dl["тема.заглушка"]);
  6. // Костяк.
  7. мж.ресурсы = [
  8. [Б + "модели/фишка/2019-09-08.osgt", "mod"],
  9. [Б + "текстуры/заглушка.png", "tex.stub"],
  10. [Б + "шейдеры/освещение-изображение.vert", "ver"],
  11. [Б + "шейдеры/освещение-изображение.frag", "fra"],
  12. [Б + "раскладки/X_shaped.layout", "lay"],
  13. ];
  14. мж.начали = new Уведомитель();
  15. мж.начать = function()
  16. {
  17. муром.камера.позиция = [0, -70, 0];
  18. муром.камера.вращение = [90, 0, 0];
  19. мж.сцена = муром.узлы.создатьСферу("sce", 0);
  20. мж.сцена.вращение = [60, 0, 0];
  21. var мир = муром.узлы.узел("mir");
  22. мир.добавитьДитя(мж.сцена);
  23. мж.начали.уведомить();
  24. };
  25. мж.задатьНейтральныйМатериал = function()
  26. {
  27. var мат = муром.материалы.создатьМатериал("N");
  28. var изо = муром.ресурсы.ресурс("tex.stub");
  29. мат.задатьТекстуру("image", изо);
  30. var вер = муром.ресурсы.ресурс("ver").содержимое;
  31. var фра = муром.ресурсы.ресурс("fra").содержимое;
  32. мат.задатьШейдеры(вер, фра);
  33. мж.сцена.задатьМатериал(мат);
  34. };
  35. мж.разобратьРаскладку = function()
  36. {
  37. var содержимое = муром.ресурсы.ресурс("lay").содержимое;
  38. мж.раскладка = new Раскладка();
  39. мж.раскладка.разобрать(содержимое);
  40. };
  41. мж.фишки = [];
  42. мж.создатьФишки = function()
  43. {
  44. for (var номер in мж.раскладка.позиции)
  45. {
  46. var ф = new Фишка();
  47. ф.позиция = мж.раскладка.позиции[номер];
  48. мж.фишки.push(ф);
  49. }
  50. };
  51. мж.размерФишки = {
  52. "ширина": 2.0,
  53. "высота": 3.0,
  54. "глубина": 1.0,
  55. };
  56. мж.создатьУзлыФишек = function()
  57. {
  58. const шагФишки = 2.0;
  59. const коэффициенты = {
  60. "x": мж.размерФишки.ширина / шагФишки,
  61. "y": -мж.размерФишки.высота / шагФишки,
  62. "z": мж.размерФишки.глубина,
  63. };
  64. var мод = муром.ресурсы.ресурс("mod");
  65. for (var номер in мж.фишки)
  66. {
  67. var имя = номер.toString();
  68. var узел = муром.узлы.создатьУзел(имя, мод);
  69. мж.сцена.добавитьДитя(узел);
  70. var ф = мж.фишки[номер];
  71. var п = ф.позиция;
  72. узел.позиция = [
  73. п[2] * коэффициенты.x,
  74. п[1] * коэффициенты.y,
  75. п[0] * коэффициенты.z,
  76. ];
  77. ф.узел = узел;
  78. }
  79. };
  80. мж.центрироватьСцену = function()
  81. {
  82. var границы = {
  83. "лево": 1000,
  84. "право": -1000,
  85. "верх": -1000,
  86. "низ": 1000,
  87. };
  88. for (var номер in мж.фишки)
  89. {
  90. const ф = мж.фишки[номер];
  91. const x = ф.узел.позиция[0];
  92. const y = ф.узел.позиция[1];
  93. if (x < границы.лево)
  94. {
  95. границы.лево = x;
  96. }
  97. if (x > границы.право)
  98. {
  99. границы.право = x;
  100. }
  101. if (y < границы.низ)
  102. {
  103. границы.низ = y;
  104. }
  105. if (y > границы.верх)
  106. {
  107. границы.верх = y;
  108. }
  109. }
  110. const ширина = границы.право - границы.лево + мж.размерФишки.ширина;
  111. const высота = границы.верх - границы.низ + мж.размерФишки.высота;
  112. мж.сцена.позиция = [-ширина / 2.0, 0, высота / 2.0];
  113. };
  114. // http://www.rubl.com/rules/mahjong-solitaire-rules.html
  115. мж.задатьФишкамГруппыПоследовательно = function()
  116. {
  117. var группы = [];
  118. // Генерируем группы.
  119. const группВсего = 42;
  120. const группПо4Дубля = 34;
  121. for (var г = 0; г < группВсего; ++г)
  122. {
  123. const четыреДубля = (г < группПо4Дубля);
  124. const колвоДублей = четыреДубля ? 4 : 1;
  125. for (var д = 0; д < колвоДублей; ++д)
  126. {
  127. группы.push(г);
  128. }
  129. }
  130. // Задаём.
  131. for (var номер in мж.фишки)
  132. {
  133. var ф = мж.фишки[номер];
  134. ф.группа = Number(группы[номер]);
  135. }
  136. };
  137. мж.применитьТемуФишек = function()
  138. {
  139. const R = RR();
  140. // Создать текстуры.
  141. var обозначения = [];
  142. var текстуры = [];
  143. for (var номер in R)
  144. {
  145. var обозначение = R[номер][0];
  146. обозначения.push(обозначение);
  147. var имя = "tile.tex/" + обозначение;
  148. var содержимое = R[номер][1];
  149. var текстура = муром.ресурсы.создатьРесурс(имя, содержимое);
  150. текстуры.push(текстура);
  151. }
  152. // Создать материалы.
  153. var нейтраль = [];
  154. var выбор = [];
  155. var вер = муром.ресурсы.ресурс("ver").содержимое;
  156. var фра = муром.ресурсы.ресурс("fra").содержимое;
  157. // Нейтраль.
  158. for (var номер = 0; номер < 42; ++номер)
  159. {
  160. var имя = "tile.mat/" + обозначения[номер];
  161. var изо = текстуры[номер];
  162. var мат = муром.материалы.создатьМатериал(имя);
  163. мат.задатьТекстуру("image", изо);
  164. мат.задатьШейдеры(вер, фра);
  165. нейтраль.push(мат);
  166. }
  167. // Выбор.
  168. for (var номер = 42; номер < 83; ++номер)
  169. {
  170. var имя = "tile.mat/" + обозначения[номер];
  171. var изо = текстуры[номер];
  172. var мат = муром.материалы.создатьМатериал(имя);
  173. мат.задатьТекстуру("image", изо);
  174. мат.задатьШейдеры(вер, фра);
  175. выбор.push(мат);
  176. }
  177. // Применить тему.
  178. for (var номер in мж.фишки)
  179. {
  180. var ф = мж.фишки[номер];
  181. ф.нейтраль = нейтраль[ф.группа];
  182. ф.выбор = выбор[ф.группа];
  183. ф.показатьНейтраль();
  184. }
  185. };
  186. мж.маскаВыбора = 0x2;
  187. мж.сделатьФишкиВыбираемыми = function()
  188. {
  189. for (var номер in мж.фишки)
  190. {
  191. var ф = мж.фишки[номер];
  192. ф.узел.задатьМаску(мж.маскаВыбора);
  193. }
  194. }
  195. мж.индексПозиции = function(п)
  196. {
  197. return п[0] * 1000000 + п[1] * 1000 + п[2];
  198. };
  199. мж.индексПозиций = {};
  200. мж.проиндексироватьПозиции = function()
  201. {
  202. for (var номер in мж.фишки)
  203. {
  204. var ф = мж.фишки[номер];
  205. var и = мж.индексПозиции(ф.позиция);
  206. мж.индексПозиций[и] = true;
  207. }
  208. };
  209. мж.естьСоседи = function(позиция, смещениеПоля, смещениеСтолбца)
  210. {
  211. for (var смещениеРяда = -1; смещениеРяда <= 1; ++смещениеРяда)
  212. {
  213. var сосед = [
  214. Number(позиция[0]) + смещениеПоля,
  215. Number(позиция[1]) + смещениеРяда,
  216. Number(позиция[2]) + смещениеСтолбца,
  217. ];
  218. var и = мж.индексПозиции(сосед);
  219. if (и in мж.индексПозиций)
  220. {
  221. return true;
  222. }
  223. }
  224. // Соседей нет.
  225. return false;
  226. };
  227. мж.можноВыбратьФишку = function(ф)
  228. {
  229. // Проверяем наличие фишек с левой и правой сторон одновременно.
  230. var слева = this.естьСоседи(ф.позиция, 0, -2);
  231. var справа = this.естьСоседи(ф.позиция, 0, 2);
  232. if (слева && справа)
  233. {
  234. return false;
  235. }
  236. // Проверяем наличие фишек непосредственно сверху.
  237. for (var смещение = -1; смещение <= 1; ++смещение)
  238. {
  239. if (this.естьСоседи(ф.позиция, 1, смещение))
  240. {
  241. return false;
  242. }
  243. }
  244. // Фишка не заблокирована.
  245. return true;
  246. };
  247. мж.номерВыбраннойФишки = null;
  248. мж.выбраннаяФишка = null;
  249. мж.выбранаФишка = new Уведомитель();
  250. мж.выбратьФишку = function()
  251. {
  252. // Определить щелчок.
  253. if (муром.мышь.нажатыеКнопки.length == 1)
  254. {
  255. var узел =
  256. муром.камера.узелВПозиции(
  257. муром.мышь.позиция,
  258. мж.маскаВыбора
  259. );
  260. if (узел)
  261. {
  262. var номер = Number(узел.имя);
  263. var ф = мж.фишки[номер];
  264. if (мж.можноВыбратьФишку(ф))
  265. {
  266. мж.номерВыбраннойФишки = номер;
  267. мж.выбраннаяФишка = ф;
  268. мж.выбранаФишка.уведомить();
  269. }
  270. }
  271. }
  272. };
  273. мж.отладитьВыборФишки = function()
  274. {
  275. мж.выбранаФишка.подписать(function(){
  276. console.log(
  277. "Выбор. Номер: '" +
  278. мж.номерВыбраннойФишки +
  279. "' Группа: '" +
  280. мж.выбраннаяФишка.группа +
  281. "'"
  282. );
  283. });
  284. };
  285. мж.показатьВыбраннуюФишку = function()
  286. {
  287. мж.выбраннаяФишка.показатьВыбор();
  288. };
  289. мж.выбранныеФишки = [];
  290. мж.фишкиРазличаются = new Уведомитель();
  291. мж.фишкиСовпадают = new Уведомитель();
  292. мж.фишкиСравнили = new Уведомитель();
  293. мж.сравнитьВыбранныеФишки = function()
  294. {
  295. // Собираем.
  296. мж.выбранныеФишки.push(мж.выбраннаяФишка);
  297. // Удостоверяемся в наличии пары фишек.
  298. if (мж.выбранныеФишки.length != 2)
  299. {
  300. return;
  301. }
  302. var ф1 = мж.выбранныеФишки[0];
  303. var ф2 = мж.выбранныеФишки[1];
  304. // Убираем дубликат при двойном выборе одной фишки.
  305. if (ф1.имя == ф2.имя)
  306. {
  307. мж.выбранныеФишки.shift();
  308. return;
  309. }
  310. // Сравниваем.
  311. if (ф1.группа == ф2.группа)
  312. {
  313. мж.фишкиСовпадают.уведомить();
  314. }
  315. else
  316. {
  317. мж.фишкиРазличаются.уведомить();
  318. }
  319. мж.фишкиСравнили.уведомить();
  320. };
  321. мж.отладитьСравнениеФишек = function()
  322. {
  323. мж.фишкиСовпадают.подписать(function(){
  324. console.log("Фишки совпадают");
  325. });
  326. мж.фишкиРазличаются.подписать(function(){
  327. console.log("Фишки различаются");
  328. });
  329. };
  330. мж.скрытьСовпадающиеФишки = function()
  331. {
  332. мж.выбранныеФишки[0].скрыть();
  333. мж.выбранныеФишки[1].скрыть();
  334. };
  335. мж.убратьСовпадающиеФишки = function()
  336. {
  337. var ф1 = мж.выбранныеФишки[0];
  338. var ф2 = мж.выбранныеФишки[1];
  339. var и1 = мж.индексПозиции(ф1.позиция);
  340. var и2 = мж.индексПозиции(ф2.позиция);
  341. delete мж.индексПозиций[и1];
  342. delete мж.индексПозиций[и2];
  343. };
  344. мж.очиститьОтображениеВыбора = function()
  345. {
  346. мж.выбранныеФишки[0].показатьНейтраль();
  347. мж.выбранныеФишки[1].показатьНейтраль();
  348. };
  349. мж.очиститьВыбор = function()
  350. {
  351. мж.номерВыбраннойФишки = 0;
  352. мж.выбраннаяФишка = null;
  353. мж.выбранныеФишки = [];
  354. };
  355. мж.оставшиесяФишки = [];
  356. мж.задатьОставшиесяФишки = function()
  357. {
  358. мж.оставшиесяФишки = мж.фишки.slice();
  359. };
  360. мж.обновилиОставшиесяФишки = new Уведомитель();
  361. мж.обновитьОставшиесяФишки = function()
  362. {
  363. var и1 = мж.оставшиесяФишки.indexOf(мж.выбранныеФишки[0]);
  364. мж.оставшиесяФишки.splice(и1, 1);
  365. var и2 = мж.оставшиесяФишки.indexOf(мж.выбранныеФишки[1]);
  366. мж.оставшиесяФишки.splice(и2, 1);
  367. мж.обновилиОставшиесяФишки.уведомить();
  368. };
  369. мж.доступныеДляВыбораФишки = function()
  370. {
  371. var фишки = [];
  372. for (var номер in мж.оставшиесяФишки)
  373. {
  374. var ф = мж.оставшиесяФишки[номер];
  375. if (мж.можноВыбратьФишку(ф))
  376. {
  377. фишки.push(ф);
  378. }
  379. }
  380. return фишки;
  381. };
  382. мж.естьХод = function()
  383. {
  384. var фишки = мж.доступныеДляВыбораФишки();
  385. for (var н1 in фишки)
  386. {
  387. for (var н2 in фишки)
  388. {
  389. var ф1 = фишки[н1];
  390. var ф2 = фишки[н2];
  391. if (
  392. (ф1.имя != ф2.имя) &&
  393. (ф1.группа == ф2.группа)
  394. ) {
  395. return true;
  396. }
  397. }
  398. }
  399. return false;
  400. }
  401. мж.победа = new Уведомитель();
  402. мж.поражение = new Уведомитель();
  403. мж.проверитьЗавершение = function()
  404. {
  405. var кончилисьФишки = (мж.оставшиесяФишки.length == 0);
  406. if (кончилисьФишки)
  407. {
  408. мж.победа.уведомить();
  409. return;
  410. }
  411. if (!мж.естьХод())
  412. {
  413. мж.поражение.уведомить();
  414. }
  415. };
  416. мж.отладитьЗавершение = function()
  417. {
  418. мж.победа.подписать(function(){
  419. console.log("Игра завершена. ПОБЕДА!");
  420. });
  421. мж.поражение.подписать(function(){
  422. console.log("Игра завершена. ПОРАЖЕНИЕ :(");
  423. });
  424. };
  425. мж.задатьОтображениеЗавершения = function()
  426. {
  427. мж.победа.подписать(function(){
  428. муром.камера.цветОчистки = [0.2, 0.5, 0.2];
  429. });
  430. мж.поражение.подписать(function(){
  431. муром.камера.цветОчистки = [0.5, 0.2, 0.2];
  432. });
  433. };
  434. // Игра.
  435. мж.начали.подписатьМного([
  436. мж.задатьНейтральныйМатериал,
  437. мж.разобратьРаскладку,
  438. мж.создатьФишки,
  439. мж.создатьУзлыФишек,
  440. мж.центрироватьСцену,
  441. мж.задатьФишкамГруппыПоследовательно,
  442. мж.применитьТемуФишек,
  443. мж.сделатьФишкиВыбираемыми,
  444. мж.отладитьВыборФишки,
  445. мж.проиндексироватьПозиции,
  446. мж.отладитьСравнениеФишек,
  447. мж.задатьОставшиесяФишки,
  448. мж.отладитьЗавершение,
  449. мж.задатьОтображениеЗавершения,
  450. ]);
  451. муром.мышь.нажатыеКнопкиИзменились.подписать(мж.выбратьФишку);
  452. мж.выбранаФишка.подписатьМного([
  453. мж.показатьВыбраннуюФишку,
  454. мж.сравнитьВыбранныеФишки,
  455. ]);
  456. мж.фишкиСовпадают.подписатьМного([
  457. мж.убратьСовпадающиеФишки,
  458. мж.обновитьОставшиесяФишки,
  459. function(){
  460. setTimeout(мж.скрытьСовпадающиеФишки, 200);
  461. },
  462. ]);
  463. мж.фишкиРазличаются.подписать(function(){
  464. setTimeout(мж.очиститьОтображениеВыбора, 200);
  465. });
  466. мж.фишкиСравнили.подписать(function(){
  467. setTimeout(мж.очиститьВыбор, 200);
  468. });
  469. мж.обновилиОставшиесяФишки.подписать(мж.проверитьЗавершение);
  470. // Начать после загрузки ресурсов.
  471. муром.ресурсы.получить(мж.ресурсы, мж.начать);