Игра Маджонг | Mahjong game
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

522 行
18KB

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