From 7c0157401b418bfab6420af94e1bc3ad331ca07b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D0=BF?=
=?UTF-8?q?=D0=B5=D0=BB=D1=8C=D0=BA=D0=BE?=
Date: Tue, 12 Oct 2021 18:57:13 +0300
Subject: [PATCH] M-4.0
---
M/4.0/игра.html | 38 +++++
M/4.0/игра/050.Заголовок.js | 10 ++
M/4.0/игра/070.Виды.js | 28 ++++
M/4.0/игра/100.Изображения.js | 146 +++++++++++++++++
M/4.0/игра/120.Физика.js | 28 ++++
M/4.0/игра/140.Тела.js | 149 ++++++++++++++++++
M/4.0/игра/160.Слежение.js | 105 ++++++++++++
M/4.0/игра/180.Объекты.js | 109 +++++++++++++
M/4.0/игра/700.Игра.js | 76 +++++++++
M/4.0/игра/matter-js | 1 +
M/4.0/общее/100.События.js | 31 ++++
M/4.0/общее/150.Состояние.js | 61 +++++++
M/4.0/общее/200.функции.js | 91 +++++++++++
M/4.0/общее/base64-js | 1 +
M/4.0/общее/pako | 1 +
M/4.0/редактор.html | 110 +++++++++++++
.../редактор/100.Редактор.js | 127 +++++++++++++++
M/4.0/редактор/200.Пульт.js | 53 +++++++
M/4.0/редактор/700.Муром.js | 13 ++
M/4.0/редактор/ace | 1 +
M/4.0/редактор/uikit | 1 +
M/doc/ru/bodies.html | 10 +-
M/doc/ru/bodies.md | 7 +-
M/doc/ru/images.html | 8 +-
M/doc/ru/images.md | 4 +-
M/doc/ru/item.template | 4 +-
M/doc/ru/keys.html | 6 +-
M/doc/ru/keys.md | 4 +
M/doc/ru/objects.html | 6 +-
M/doc/ru/objects.md | 2 +-
M/doc/ru/physics.html | 111 +++++++++++++
M/doc/ru/physics.md | 18 +++
M/doc/ru/styles.html | 111 +++++++++++++
M/doc/ru/styles.md | 18 +++
M/doc/ru/title.html | 4 +-
M/doc/ru/tracking.html | 9 +-
M/doc/ru/tracking.md | 6 +-
M/index.html | 2 +-
38 files changed, 1481 insertions(+), 29 deletions(-)
create mode 100644 M/4.0/игра.html
create mode 100644 M/4.0/игра/050.Заголовок.js
create mode 100644 M/4.0/игра/070.Виды.js
create mode 100644 M/4.0/игра/100.Изображения.js
create mode 100644 M/4.0/игра/120.Физика.js
create mode 100644 M/4.0/игра/140.Тела.js
create mode 100644 M/4.0/игра/160.Слежение.js
create mode 100644 M/4.0/игра/180.Объекты.js
create mode 100644 M/4.0/игра/700.Игра.js
create mode 120000 M/4.0/игра/matter-js
create mode 100644 M/4.0/общее/100.События.js
create mode 100644 M/4.0/общее/150.Состояние.js
create mode 100644 M/4.0/общее/200.функции.js
create mode 120000 M/4.0/общее/base64-js
create mode 120000 M/4.0/общее/pako
create mode 100644 M/4.0/редактор.html
create mode 100644 M/4.0/редактор/100.Редактор.js
create mode 100644 M/4.0/редактор/200.Пульт.js
create mode 100644 M/4.0/редактор/700.Муром.js
create mode 120000 M/4.0/редактор/ace
create mode 120000 M/4.0/редактор/uikit
create mode 100644 M/doc/ru/physics.html
create mode 100644 M/doc/ru/physics.md
create mode 100644 M/doc/ru/styles.html
create mode 100644 M/doc/ru/styles.md
diff --git a/M/4.0/игра.html b/M/4.0/игра.html
new file mode 100644
index 0000000..a84f860
--- /dev/null
+++ b/M/4.0/игра.html
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/M/4.0/игра/050.Заголовок.js b/M/4.0/игра/050.Заголовок.js
new file mode 100644
index 0000000..2e47500
--- /dev/null
+++ b/M/4.0/игра/050.Заголовок.js
@@ -0,0 +1,10 @@
+function Заголовок()
+{
+ this.обработатьКлюч = function(ключ, путь, значение)
+ {
+ if (ключ == "заголовок")
+ {
+ document.title = значение;
+ }
+ }
+}
diff --git a/M/4.0/игра/070.Виды.js b/M/4.0/игра/070.Виды.js
new file mode 100644
index 0000000..c74121e
--- /dev/null
+++ b/M/4.0/игра/070.Виды.js
@@ -0,0 +1,28 @@
+function Виды(события)
+{
+ this.создать = function()
+ {
+ this.задано = {};
+ };
+
+ this.обработатьКлюч = function(ключ, путь, значение)
+ {
+ if (путь[0] != "виды")
+ {
+ return;
+ }
+
+ let имя = путь[1];
+ let свойство = путь.slice(2).join(".");
+ if (!this.задано[имя])
+ {
+ this.задано[имя] = {};
+ }
+ this.задано[имя][свойство] = значение;
+
+ события.уведомить(`виды/${имя}`);
+ };
+
+ // Конструктор.
+ this.создать();
+};
diff --git a/M/4.0/игра/100.Изображения.js b/M/4.0/игра/100.Изображения.js
new file mode 100644
index 0000000..be615c6
--- /dev/null
+++ b/M/4.0/игра/100.Изображения.js
@@ -0,0 +1,146 @@
+function Изображения(события, виды, корень)
+{
+ this.создать = function()
+ {
+ this.умолчание = {
+ пр: [0, 0, 100, 100],
+ угол: 0,
+ };
+ this.задано = {};
+ this.элементы = {};
+ события.подписать(this);
+ };
+
+ this.создатьИлиПолучитьЭлемент = function(имя)
+ {
+ var эл = this.элементы[имя];
+ if (эл)
+ {
+ return эл;
+ }
+
+ var ум = this.умолчание;
+ эл = document.createElement("div");
+ эл.id = `изображения-${имя}`;
+ эл.style.position = "absolute";
+ эл.style.display = "block";
+ // Свойства по умолчанию.
+ this.обновитьПозициюРазмерЭлемента(имя, эл);
+ эл.style.transform = `rotate(${ум.угол}rad)`;
+ this.элементы[имя] = эл;
+ корень.appendChild(эл);
+ return эл;
+ };
+
+ this.обновитьВид = function(имя, вид)
+ {
+ var эл = this.создатьИлиПолучитьЭлемент(имя);
+ let за = виды.задано[вид];
+ for (let параметр in за)
+ {
+ эл.style.setProperty(параметр, за[параметр]);
+ }
+ this.обновитьУгол(имя);
+ };
+
+ this.обновитьПозициюРазмерЭлемента = function(имя, эл)
+ {
+ let пр = this.пр(имя);
+ эл.style.left = `${пр[0]}px`;
+ эл.style.top = `${пр[1]}px`;
+ эл.style.width = `${пр[2]}px`;
+ эл.style.height = `${пр[3]}px`;
+ };
+
+ this.обновитьУгол = function(имя)
+ {
+ var за = this.задано[имя];
+ var ум = this.умолчание;
+ var эл = this.элементы[имя];
+
+ var угол = за.угол ? за.угол : ум.угол;
+ var transform = `rotate(${угол}deg) `;
+
+ let вид = за["вид"];
+ if (вид)
+ {
+ let виза = виды.задано[вид];
+ if (виза && виза["transform"])
+ {
+ transform += виза["transform"];
+ }
+ }
+
+ эл.style.transform = transform;
+ };
+
+ this.обновитьЭлемент = function(имя, свойство, значение)
+ {
+ if (!this.задано[имя])
+ {
+ this.задано[имя] = {};
+ }
+ this.задано[имя][свойство] = значение;
+ var за = this.задано[имя];
+ var эл = this.создатьИлиПолучитьЭлемент(имя);
+
+ if (свойство.startsWith("пр"))
+ {
+ this.обновитьПозициюРазмерЭлемента(имя, эл);
+ }
+ else if (
+ (свойство == "угол") ||
+ (свойство == "вид")
+ ) {
+ this.обновитьВид(имя, за["вид"]);
+ }
+ };
+
+ this.обновитьЭлементыВида = function(вид)
+ {
+ for (let имя in this.задано)
+ {
+ let заданныйВид = this.задано[имя]["вид"];
+ if (заданныйВид && заданныйВид == вид)
+ {
+ this.обновитьВид(имя, вид);
+ }
+ }
+ };
+
+ this.обработатьКлюч = function(ключ, путь, значение)
+ {
+ if (путь[0] == "изображения")
+ {
+ var имя = путь[1];
+ var свойство = путь.slice(2).join(".");
+ this.обновитьЭлемент(имя, свойство, значение);
+ }
+ };
+
+ this.обработатьСобытие = function(событие)
+ {
+ let префикс = "виды/";
+ if (событие.startsWith(префикс))
+ {
+ let вид = событие.substring(префикс.length);
+ this.обновитьЭлементыВида(вид);
+ }
+ };
+
+ this.пр = function(имя)
+ {
+ let за = this.задано[имя];
+ let пр = this.умолчание.пр;
+ return [
+ за["пр.0"] ? за["пр.0"] : пр[0],
+ за["пр.1"] ? за["пр.1"] : пр[1],
+ за["пр.2"] ? за["пр.2"] : пр[2],
+ за["пр.3"] ? за["пр.3"] : пр[3],
+ ];
+ };
+
+
+ // Конструктор.
+ this.создать();
+}
diff --git a/M/4.0/игра/120.Физика.js b/M/4.0/игра/120.Физика.js
new file mode 100644
index 0000000..d75fc12
--- /dev/null
+++ b/M/4.0/игра/120.Физика.js
@@ -0,0 +1,28 @@
+function Физика(события)
+{
+ this.создать = function()
+ {
+ this.задано = {};
+ };
+
+ this.обработатьКлюч = function(ключ, путь, значение)
+ {
+ if (путь[0] != "физика")
+ {
+ return;
+ }
+
+ let имя = путь[1];
+ let свойство = путь.slice(2).join(".");
+ if (!this.задано[имя])
+ {
+ this.задано[имя] = {};
+ }
+ this.задано[имя][свойство] = значение;
+
+ события.уведомить(`физика/${имя}`);
+ };
+
+ // Конструктор.
+ this.создать();
+};
diff --git a/M/4.0/игра/140.Тела.js b/M/4.0/игра/140.Тела.js
new file mode 100644
index 0000000..2e9a330
--- /dev/null
+++ b/M/4.0/игра/140.Тела.js
@@ -0,0 +1,149 @@
+function Тела(события, физика, физмир)
+{
+ this.создать = function()
+ {
+ this.умолчание = {
+ пр: [0, 0, 40, 20],
+ части: [],
+ часть: false,
+ };
+
+ this.задано = {};
+ this.тела = {};
+ this.имена = {};
+ события.подписать(this);
+ };
+
+ this.именаЧастей = function(за)
+ {
+ var имена = [];
+ for (var ключ in за)
+ {
+ if (ключ.startsWith("части"))
+ {
+ имена.push(за[ключ]);
+ }
+ }
+ return имена;
+ };
+
+ this.обработатьКлюч = function(ключ, путь, значение)
+ {
+ if (путь[0] != "тела")
+ {
+ return;
+ }
+
+ var имя = путь[1];
+ var свойство = путь.slice(2).join(".");
+ if (!this.задано[имя])
+ {
+ this.задано[имя] = {};
+ }
+ this.задано[имя][свойство] = значение;
+
+ this.пересоздатьТело(имя);
+ };
+
+ this.обработатьСобытие = function(событие)
+ {
+ let префикс = "физика/";
+ if (событие.startsWith(префикс))
+ {
+ let физ = событие.substring(префикс.length);
+ this.пересоздатьТелаФизики(физ);
+ }
+ };
+
+ this.пересоздатьТелаФизики = function(физ)
+ {
+ for (let имя in this.задано)
+ {
+ let заданнаяФизика = this.задано[имя]["физика"];
+ if (заданнаяФизика && заданнаяФизика == физ)
+ {
+ this.пересоздатьТело(имя);
+ }
+ }
+ };
+
+ this.пересоздатьТело = function(имя)
+ {
+ // Удаляем старое тело.
+ if (имя in this.тела)
+ {
+ var тело = this.тела[имя];
+ delete this.имена[тело.id];
+ // Всегда удаляем из мира: и составные, и несоставные тела.
+ Matter.Composite.remove(физмир, тело);
+ }
+
+ var за = this.задано[имя];
+ var ум = this.умолчание;
+ let пр = this.пр(имя);
+ // Переводим x,y из левого верхнего угла в центр.
+ пр[0] = пр[0] + пр[2] / 2.0;
+ пр[1] = пр[1] + пр[3] / 2.0;
+ // Параметры тела.
+ var параметры = {};
+ if (за.физика)
+ {
+ let заф = физика.задано[за.физика];
+ for (let параметр in заф)
+ {
+ мир.задатьПолныйКлюч(параметры, параметр, заф[параметр]);
+ }
+ }
+
+ var тело = null;
+ // Создаём новое составное тело.
+ if (за["части.0"])
+ {
+ параметры["parts"] = this.телаЧастей(this.именаЧастей(за));
+ тело = Matter.Body.create(параметры);
+ }
+ // Создаём новое несоставное тело.
+ // Вполне может быть частью другого составного тела.
+ else
+ {
+ тело = Matter.Bodies.rectangle(пр[0], пр[1], пр[2], пр[3], параметры);
+ }
+ this.тела[имя] = тело;
+ this.имена[тело.id] = имя;
+
+ // Добавляем тело в физический мир, если оно не является частью другого составного тела.
+ var часть = за.часть ? за.часть : ум.часть;
+ if (!часть)
+ {
+ Matter.Composite.add(физмир, тело);
+ }
+ };
+
+ this.пр = function(имя)
+ {
+ let за = this.задано[имя];
+ let пр = this.умолчание.пр;
+ return [
+ за["пр.0"] ? за["пр.0"] : пр[0],
+ за["пр.1"] ? за["пр.1"] : пр[1],
+ за["пр.2"] ? за["пр.2"] : пр[2],
+ за["пр.3"] ? за["пр.3"] : пр[3],
+ ];
+ };
+
+ this.телаЧастей = function(имена)
+ {
+ var тела = [];
+ for (var н in имена)
+ {
+ var имя = имена[н];
+ var тело = this.тела[имя];
+ тела.push(тело);
+ }
+ return тела;
+ };
+
+ // Конструктор.
+ this.создать();
+}
+
diff --git a/M/4.0/игра/160.Слежение.js b/M/4.0/игра/160.Слежение.js
new file mode 100644
index 0000000..0e7a15c
--- /dev/null
+++ b/M/4.0/игра/160.Слежение.js
@@ -0,0 +1,105 @@
+function Слежение(состояние, изображения, тела)
+{
+ this.создать = function()
+ {
+ this.умолчание = {
+ смещение: [0, 0],
+ скорость: 1,
+ предел: 0,
+ };
+ this.задано = {};
+ };
+
+ this.обновить = function()
+ {
+ for (var имя in this.задано)
+ {
+ this.расположитьИзображение(имя);
+ }
+ };
+
+ this.обработатьКлюч = function(ключ, путь, значение)
+ {
+ if (путь[0] != "слежение")
+ {
+ return;
+ }
+
+ var имя = путь[1];
+ var свойство = путь.slice(2).join(".");
+ if (!this.задано[имя])
+ {
+ this.задано[имя] = {};
+ }
+ this.задано[имя][свойство] = значение;
+ };
+
+ this.расположитьИзображение = function(имя)
+ {
+ let за = this.задано[имя];
+ let тело = тела.тела[за.тело];
+ let элемент = изображения.элементы[за.изображение];
+ if (!тело || !элемент)
+ {
+ return;
+ }
+
+ let ум = this.умолчание;
+
+ // Параметры.
+ var скорость = за.скорость ? за.скорость : ум.скорость;
+ var смещениеX = за["смещение.0"] ? за["смещение.0"] : ум.смещение[0];
+ var смещениеY = за["смещение.1"] ? за["смещение.1"] : ум.смещение[1];
+ var предел = за.предел ? за.предел : ум.предел;
+
+ // Текущая позиция.
+ var x0 = 0;
+ if (элемент.dataset.слежениеX)
+ {
+ x0 = элемент.dataset.слежениеX;
+ }
+ var y0 = 0;
+ if (элемент.dataset.слежениеY)
+ {
+ y0 = элемент.dataset.слежениеY;
+ }
+
+ // Целевая позиция.
+ var x1 = тело.position.x + смещениеX;
+ var y1 = тело.position.y + смещениеY;
+ // Radians -> Degrees.
+ var угол = тело.angle * 180 / Math.PI;
+
+ // Устанавливаемая плавно позиция.
+ var x = this.lerp(x0, x1, скорость, предел);
+ var y = this.lerp(y0, y1, скорость, предел);
+
+ элемент.dataset.слежениеX = x;
+ элемент.dataset.слежениеY = y;
+
+ var описание = {
+ пр: [x, y],
+ угол: угол,
+ };
+ состояние.разобрать({
+ изображения: {
+ [за.изображение]: описание,
+ },
+ });
+ };
+
+ this.lerp = function(v0, v1, t, предел) {
+ // Убираем мельтешение в случае наличия предела.
+ if (предел)
+ {
+ var delta = Math.abs(v0 - v1);
+ if (delta < предел) {
+ return v0;
+ }
+ }
+ return v0 * (1 - t) + v1 * t;
+ };
+
+ // Конструктор.
+ this.создать();
+};
diff --git a/M/4.0/игра/180.Объекты.js b/M/4.0/игра/180.Объекты.js
new file mode 100644
index 0000000..88e7677
--- /dev/null
+++ b/M/4.0/игра/180.Объекты.js
@@ -0,0 +1,109 @@
+function Объекты(состояние)
+{
+ this.создать = function()
+ {
+ this.умолчание = {
+ пр: [0, 0, 40, 20],
+ };
+ this.задано = {};
+ };
+
+ this.обновитьИзображение = function(имя, пр, вид)
+ {
+ состояние.разобрать({
+ изображения: {
+ [имя]: {
+ пр: пр,
+ вид: вид,
+ },
+ },
+ });
+ };
+
+ this.обновитьПозициюРазмер = function(имя, пр)
+ {
+ состояние.разобрать({
+ тела: {
+ [имя]: {
+ пр: пр,
+ },
+ },
+ });
+ };
+
+ this.обновитьСлежение = function(имя, пр)
+ {
+ состояние.разобрать({
+ слежение: {
+ [имя]: {
+ изображение: имя,
+ тело: имя,
+ смещение: [-пр[2] / 2, -пр[3] / 2],
+ },
+ },
+ });
+ };
+
+ this.обновитьФизику = function(имя, значение)
+ {
+ состояние.разобрать({
+ тела: {
+ [имя]: {
+ физика: значение,
+ },
+ },
+ });
+ };
+
+ this.обработатьКлюч = function(ключ, путь, значение)
+ {
+ if (путь[0] != "объекты")
+ {
+ return;
+ }
+
+ let имя = путь[1];
+ let свойство = путь.slice(2).join(".");
+ if (!this.задано[имя])
+ {
+ this.задано[имя] = {};
+ }
+ this.задано[имя][свойство] = значение;
+
+ let производноеИмя = `объекты-${имя}`;
+
+ if (свойство.startsWith("пр."))
+ {
+ let пр = this.пр(имя);
+ this.обновитьПозициюРазмер(производноеИмя, пр);
+ this.обновитьСлежение(производноеИмя, пр);
+ let вид = this.задано[имя]["вид"];
+ this.обновитьИзображение(производноеИмя, пр, вид);
+ }
+ else if (свойство == "физика")
+ {
+ this.обновитьФизику(производноеИмя, значение);
+ }
+ else if (свойство == "вид")
+ {
+ let пр = this.пр(имя);
+ let вид = this.задано[имя]["вид"];
+ this.обновитьИзображение(производноеИмя, пр, вид);
+ }
+ };
+
+ this.пр = function(имя)
+ {
+ let за = this.задано[имя];
+ let пр = this.умолчание.пр;
+ return [
+ за["пр.0"] ? за["пр.0"] : пр[0],
+ за["пр.1"] ? за["пр.1"] : пр[1],
+ за["пр.2"] ? за["пр.2"] : пр[2],
+ за["пр.3"] ? за["пр.3"] : пр[3],
+ ];
+ };
+
+ // Конструктор.
+ this.создать();
+};
diff --git a/M/4.0/игра/700.Игра.js b/M/4.0/игра/700.Игра.js
new file mode 100644
index 0000000..5cfdeac
--- /dev/null
+++ b/M/4.0/игра/700.Игра.js
@@ -0,0 +1,76 @@
+function Игра()
+{
+ this.создать = function()
+ {
+ мир.физдвижок = Matter.Engine.create({
+ enableSleeping: true,
+ gravity: {
+ y: 2,
+ },
+ });
+ var корень = document.getElementById("корень");
+ мир.события = new События();
+ мир.состояние = new Состояние();
+ мир.состояние.обработчик = (к, п, з) => { this.обработатьКлюч(к, п, з) };
+ мир.виды = new Виды(мир.события);
+ мир.изображения = new Изображения(мир.события, мир.виды, корень),
+ мир.физика = new Физика(мир.события);
+ мир.тела = new Тела(мир.события, мир.физика, мир.физдвижок.world);
+ мир.слежение = new Слежение(мир.состояние, мир.изображения, мир.тела);
+ мир.объекты = new Объекты(мир.состояние);
+ this.ключники = [
+ new Заголовок(),
+ мир.виды,
+ мир.изображения,
+ мир.физика,
+ мир.тела,
+ мир.слежение,
+ мир.объекты,
+ ];
+
+ var z64 = мир.параметрыЗапуска()["z64"];
+ this.исполнитьКод(z64);
+ this.отслеживатьОбновленияКода();
+ this.обновить();
+ };
+
+ this.исполнитьКод = function(z64)
+ {
+ if (z64)
+ {
+ var код = мир.изZ64(z64);
+ eval(код);
+ }
+ };
+
+ this.обновить = function()
+ {
+ Matter.Engine.update(мир.физдвижок);
+ мир.слежение.обновить();
+
+ var тут = this;
+ requestAnimationFrame(function() {
+ тут.обновить();
+ });
+ };
+
+ this.обработатьКлюч = function(ключ, путь, значение)
+ {
+ for (var номер in this.ключники)
+ {
+ var ключник = this.ключники[номер];
+ ключник.обработатьКлюч(ключ, путь, значение);
+ }
+ };
+
+ this.отслеживатьОбновленияКода = function()
+ {
+ var тут = this;
+ window.addEventListener("message", function(событие) {
+ тут.исполнитьКод(событие.data);
+ });
+ };
+
+ // Конструктор.
+ this.создать();
+}
diff --git a/M/4.0/игра/matter-js b/M/4.0/игра/matter-js
new file mode 120000
index 0000000..3a19a2e
--- /dev/null
+++ b/M/4.0/игра/matter-js
@@ -0,0 +1 @@
+../../стороннее/matter-js
\ No newline at end of file
diff --git a/M/4.0/общее/100.События.js b/M/4.0/общее/100.События.js
new file mode 100644
index 0000000..19f8ecf
--- /dev/null
+++ b/M/4.0/общее/100.События.js
@@ -0,0 +1,31 @@
+function События()
+{
+ this.создать = function()
+ {
+ this.обработчики = [];
+ };
+
+ this.подписать = function(обработчик)
+ {
+ this.обработчики.push(обработчик);
+ };
+
+ this.отписать = function(обработчик) {
+ var номер = this.обработчики.indexOf(обработчик);
+ if (номер != -1)
+ {
+ this.обработчики.splice(номер, 1);
+ }
+ };
+
+ this.уведомить = function(событие) {
+ for (var номер in this.обработчики)
+ {
+ var обработчик = this.обработчики[номер];
+ обработчик.обработатьСобытие(событие);
+ }
+ };
+
+ // Конструктор.
+ this.создать();
+}
diff --git a/M/4.0/общее/150.Состояние.js b/M/4.0/общее/150.Состояние.js
new file mode 100644
index 0000000..5004890
--- /dev/null
+++ b/M/4.0/общее/150.Состояние.js
@@ -0,0 +1,61 @@
+function Состояние()
+{
+ this.создать = function()
+ {
+ this.обработчик = null;
+ this.значения = {};
+ };
+
+ this.выпрямить = function(obj)
+ {
+ // https://stackoverflow.com/a/42121920
+ var newObj = {};
+ for (var key in obj)
+ {
+ if (typeof obj[key] === 'object' && obj[key] !== null)
+ {
+ var temp = this.выпрямить(obj[key])
+ for (var key2 in temp)
+ {
+ newObj[key + "." + key2] = temp[key2];
+ }
+ }
+ else
+ {
+ newObj[key] = obj[key];
+ }
+ }
+ return newObj;
+ };
+
+ this.лишьНовыеЗначения = function(значения) {
+ var новые = {};
+ for (var ключ in значения)
+ {
+ var было = this.значения[ключ];
+ var стало = значения[ключ];
+ if (!(было != null && было == стало))
+ {
+ новые[ключ] = стало;
+ this.значения[ключ] = стало;
+ }
+ }
+ return новые;
+ };
+
+ this.разобрать = function(словарь) {
+ var значения = this.лишьНовыеЗначения(this.выпрямить(словарь));
+ for (var ключ in значения)
+ {
+ var путь = ключ.split(".");
+ var значение = значения[ключ];
+ if (this.обработчик)
+ {
+ this.обработчик(ключ, путь, значение);
+ }
+ }
+ };
+
+ // Конструктор.
+ this.создать();
+}
diff --git a/M/4.0/общее/200.функции.js b/M/4.0/общее/200.функции.js
new file mode 100644
index 0000000..cca058c
--- /dev/null
+++ b/M/4.0/общее/200.функции.js
@@ -0,0 +1,91 @@
+var мир = {};
+
+мир.uuid = function()
+{
+ // https://stackoverflow.com/a/2117523
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
+ /[xy]/g,
+ function(c)
+ {
+ var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
+ return v.toString(16);
+ }
+ );
+};
+
+мир.вZ64 = function(строка)
+{
+ var байты = new TextEncoder("utf-8").encode(строка);
+ var архив = pako.deflate(байты, { to: 'string' });
+ return base64js.fromByteArray(архив);
+};
+
+мир.изZ64 = function(строка)
+{
+ var архив = base64js.toByteArray(строка);
+ var байты = pako.inflate(архив);
+ return new TextDecoder("utf-8").decode(байты);
+};
+
+мир.задатьПолныйКлюч = function(словарь, полныйКлюч, значение) {
+ var путь = полныйКлюч.split(".");
+ var пройти = путь.length - 1;
+ var вложенность = словарь;
+ for (var номер = 0; номер < пройти; ++номер)
+ {
+ var ключ = путь[номер];
+ if (!(ключ in вложенность))
+ {
+ вложенность[ключ] = {};
+ вложенность = вложенность[ключ];
+ }
+ }
+ // Значение в конце вложенности.
+ var ключ = путь[пройти];
+ вложенность[ключ] = значение;
+};
+
+мир.назначитьКнопкамСобытия = function(события, список)
+{
+ for (var номер in список)
+ {
+ const пара = список[номер];
+ var кнопка = document.getElementById(пара[0]);
+ кнопка.addEventListener("click", function(_) {
+ события.уведомить(пара[1]);
+ });
+ }
+};
+
+мир.параметрыЗапуска = function()
+{
+ var параметры = {};
+ var запрос = window.location.search.substring(1);
+ var аргументы = запрос.split("&");
+ for (var номер in аргументы)
+ {
+ var арг = аргументы[номер];
+ var позицияЗнака = арг.indexOf("=");
+ // Лишь ключ.
+ if (позицияЗнака == -1)
+ {
+ var ключ = decodeURIComponent(арг);
+ параметры[ключ] = null;
+ }
+ // Ключ со значением.
+ else
+ {
+ var сыройКлюч = арг.slice(0, позицияЗнака);
+ var сыроеЗначение = арг.slice(позицияЗнака + 1);
+ var ключ = decodeURIComponent(сыройКлюч);
+ var значение = decodeURIComponent(сыроеЗначение);
+ параметры[ключ] = значение;
+ }
+ }
+ return параметры;
+};
+
+мир.разобрать = function(словарь)
+{
+ мир.состояние.разобрать(словарь);
+};
diff --git a/M/4.0/общее/base64-js b/M/4.0/общее/base64-js
new file mode 120000
index 0000000..a3367c1
--- /dev/null
+++ b/M/4.0/общее/base64-js
@@ -0,0 +1 @@
+../../стороннее/base64-js
\ No newline at end of file
diff --git a/M/4.0/общее/pako b/M/4.0/общее/pako
new file mode 120000
index 0000000..504d162
--- /dev/null
+++ b/M/4.0/общее/pako
@@ -0,0 +1 @@
+../../стороннее/pako
\ No newline at end of file
diff --git a/M/4.0/редактор.html b/M/4.0/редактор.html
new file mode 100644
index 0000000..4959542
--- /dev/null
+++ b/M/4.0/редактор.html
@@ -0,0 +1,110 @@
+
+
+
+
+ M-4.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 4.0
+ ???
+
+
+ 3.5
+ объекты
+
+
+ 3.4
+ Документация
+
+
+ 3.3
+ тела, слежение
+
+
+ 3.2
+ мир.параметрыЗапуска, мир.разобрать, заголовок, изображения, состояние, физика
+
+
+ 3.1
+ Кнопки перезапуска, копирования ссылки, запуска в отдельной вкладке
+
+
+ 3.0
+ Проверка введённого кода на отсутствие синтаксических ошибок
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/M/4.0/редактор/100.Редактор.js b/M/4.0/редактор/100.Редактор.js
new file mode 100644
index 0000000..430d3a8
--- /dev/null
+++ b/M/4.0/редактор/100.Редактор.js
@@ -0,0 +1,127 @@
+function Редактор(события, имяРедактора)
+{
+ this.создать = function()
+ {
+ this.первоначальныйЗаголовок = document.title;
+ this.установитьAce();
+ this.улавливатьЗавершениеРедактирования();
+ this.задатьКодПриЗапуске();
+ события.подписать(this);
+ };
+
+ this.задатьКодПриЗапуске = function()
+ {
+ var z64 = мир.параметрыЗапуска()["z64"];
+ if (z64)
+ {
+ var код = мир.изZ64(z64);
+ this.ace.session.setValue(код);
+ }
+ };
+
+ this.запуститьОтдельно = function()
+ {
+ var содержимое = this.ace.session.getValue();
+ var z64 = мир.вZ64(содержимое);
+ var путь = window.location.pathname + "/../игра.html?z64=" + z64;
+ window.open(путь);
+ };
+
+ this.исполнитьКод = function()
+ {
+ var содержимое = this.ace.session.getValue();
+ var z64 = мир.вZ64(содержимое);
+ // Исполняем явно код лишь первый раз.
+ var проигрыватель = document.getElementById("проигрыватель");
+ if (!проигрыватель.src)
+ {
+ проигрыватель.src = "игра.html?z64=" + z64;
+ }
+ // После запуска уведомляем iframe о новом коде без перезагрузки.
+ else
+ {
+ window.frames.проигрыватель.postMessage(z64, "*");
+ }
+ };
+
+ this.обновитьАдреснуюСтрокуИЗаголовок = function()
+ {
+ var содержимое = this.ace.session.getValue();
+ var z64 = мир.вZ64(содержимое);
+ var путь = window.location.pathname + "?z64=" + z64;
+ history.pushState(null, "", путь);
+ document.title = `${this.первоначальныйЗаголовок} ${z64.length}:${z64.slice(z64.length - 5)}`;
+ };
+
+ this.обработатьСобытие = function(событие)
+ {
+ if (событие == "завершили редактирование")
+ {
+ this.обновитьАдреснуюСтрокуИЗаголовок();
+ this.проверитьКорректностьКода();
+ }
+ else if (событие == "код корректен")
+ {
+ this.исполнитьКод();
+ }
+ else if (событие == "перезапустить")
+ {
+ this.перезапуститьКод();
+ }
+ else if (событие == "запустить отдельно")
+ {
+ this.запуститьОтдельно();
+ }
+ };
+
+ this.перезапуститьКод = function()
+ {
+ var содержимое = this.ace.session.getValue();
+ var z64 = мир.вZ64(содержимое);
+ var проигрыватель = document.getElementById("проигрыватель");
+ проигрыватель.src = "игра.html?z64=" + z64;
+ };
+
+ this.проверитьКорректностьКода = function()
+ {
+ try
+ {
+ eval(this.ace.session.getValue());
+ события.уведомить("код корректен");
+ }
+ catch (ошибка)
+ {
+ события.уведомить("код некорректен");
+ }
+ };
+
+ this.улавливатьЗавершениеРедактирования = function()
+ {
+ var тут = this;
+ this.ace.session.on("change", function(дельта) {
+ const билет = мир.uuid();
+ тут.билет = билет;
+ setTimeout(
+ function()
+ {
+ if (билет == тут.билет)
+ {
+ события.уведомить("завершили редактирование");
+ }
+ },
+ 300
+ );
+ });
+ };
+
+ this.установитьAce = function()
+ {
+ var область = document.getElementById(имяРедактора);
+ this.ace = window.ace.edit(имяРедактора);
+ this.ace.session.setMode("ace/mode/javascript");
+ this.ace.session.setUseWrapMode(true);
+ };
+
+ // Конструктор.
+ this.создать();
+}
diff --git a/M/4.0/редактор/200.Пульт.js b/M/4.0/редактор/200.Пульт.js
new file mode 100644
index 0000000..42889e5
--- /dev/null
+++ b/M/4.0/редактор/200.Пульт.js
@@ -0,0 +1,53 @@
+function Пульт(события, имяОшибки, имяПерезапуска, имяОтдельно, имяСсылки, имяДокументации)
+{
+ this.создать = function()
+ {
+ var ошибка = document.getElementById(имяОшибки);
+ мир.назначитьКнопкамСобытия(
+ события,
+ [
+ [имяПерезапуска, "перезапустить"],
+ [имяОтдельно, "запустить отдельно"],
+ [имяСсылки, "скопировать ссылку"],
+ [имяДокументации, "открыть документацию"],
+ ]
+ );
+ события.подписать(this);
+ };
+
+ this.обработатьСобытие = function(событие)
+ {
+ if (событие == "код корректен")
+ {
+ this.показатьОшибку(false);
+ }
+ else if (событие == "код некорректен")
+ {
+ this.показатьОшибку(true);
+ }
+ else if (событие == "скопировать ссылку")
+ {
+ navigator.clipboard.writeText(window.location)
+ .then(() => {
+ UIkit.notification("Скопировали ссылку", { status: "success", timeout: 1000 });
+ })
+ .catch(err => {
+ UIkit.notification("Не удалось скопировать ссылку :(", { status: "danger", timeout: 5000 });
+ });
+ }
+ else if (событие == "открыть документацию")
+ {
+ var путь = window.location.pathname + "/../../doc/ru/index.html";
+ window.open(путь);
+ }
+ };
+
+
+ this.показатьОшибку = function(показать)
+ {
+ ошибка.innerHTML = показать ? " " : " ";
+ };
+
+ // Конструктор.
+ this.создать();
+}
diff --git a/M/4.0/редактор/700.Муром.js b/M/4.0/редактор/700.Муром.js
new file mode 100644
index 0000000..fa45750
--- /dev/null
+++ b/M/4.0/редактор/700.Муром.js
@@ -0,0 +1,13 @@
+function Муром()
+{
+ this.создать = function()
+ {
+ мир.события = new События();
+ мир.состояние = new Состояние();
+ мир.редактор = new Редактор(мир.события, "редактор");
+ мир.пульт = new Пульт(мир.события, "ошибка", "перезапустить", "отдельно", "ссылка", "документация");
+ };
+
+ // Конструктор.
+ this.создать();
+}
diff --git a/M/4.0/редактор/ace b/M/4.0/редактор/ace
new file mode 120000
index 0000000..0190be9
--- /dev/null
+++ b/M/4.0/редактор/ace
@@ -0,0 +1 @@
+../../стороннее/ace
\ No newline at end of file
diff --git a/M/4.0/редактор/uikit b/M/4.0/редактор/uikit
new file mode 120000
index 0000000..d60f2bc
--- /dev/null
+++ b/M/4.0/редактор/uikit
@@ -0,0 +1 @@
+../../стороннее/uikit
\ No newline at end of file
diff --git a/M/doc/ru/bodies.html b/M/doc/ru/bodies.html
index 03fab74..8e31899 100644
--- a/M/doc/ru/bodies.html
+++ b/M/doc/ru/bodies.html
@@ -78,12 +78,12 @@
}
- Документация МУРОМа-3.5
+ Документация МУРОМа 4.0
@@ -98,10 +98,12 @@
Тело представляет из себя сущность Matter.js с полной поддержкой свойств, которые можно указывать телам в Matter.js.
Пример № 1. Отображение статичной платформы и падающего на неё самовара.
-
+
Пример № 2. Составное тело с сенсором и статичная платформа.
-
+
ВНИМАНИЕ : на текущий момент слежение некорректно отрабатывает для составных объектов,
+т.к. сенсор должен находиться строго под основанием самовара.
+
diff --git a/M/doc/ru/bodies.md b/M/doc/ru/bodies.md
index 791eb80..1463009 100644
--- a/M/doc/ru/bodies.md
+++ b/M/doc/ru/bodies.md
@@ -12,13 +12,16 @@ Lang: ru
Пример № 1. Отображение статичной платформы и падающего на неё самовара.
-
+
Пример № 2. Составное тело с сенсором и статичная платформа.
-
+**ВНИМАНИЕ**: на текущий момент слежение некорректно отрабатывает для составных объектов,
+т.к. сенсор должен находиться строго под основанием самовара.
+
+
diff --git a/M/doc/ru/images.html b/M/doc/ru/images.html
index c63729d..f30dc20 100644
--- a/M/doc/ru/images.html
+++ b/M/doc/ru/images.html
@@ -78,12 +78,12 @@
}
- Документация МУРОМа-3.5
+ Документация МУРОМа 4.0
@@ -99,10 +99,10 @@
Изображение представляет из себя отдельный <div>
, который можно исследовать в отладке браузера.
Пример № 1. Отображение статичного изображения под углом.
-
+
Пример № 2. Отображение статичной платформы и падающего на неё самовара.
-
+
diff --git a/M/doc/ru/images.md b/M/doc/ru/images.md
index 8672e87..efc36e8 100644
--- a/M/doc/ru/images.md
+++ b/M/doc/ru/images.md
@@ -14,13 +14,13 @@ Lang: ru
Пример № 1. Отображение статичного изображения под углом.
-
+
Пример № 2. Отображение статичной платформы и падающего на неё самовара.
-
+
diff --git a/M/doc/ru/item.template b/M/doc/ru/item.template
index 12e9971..9d2dafe 100644
--- a/M/doc/ru/item.template
+++ b/M/doc/ru/item.template
@@ -78,12 +78,12 @@
}
- Документация МУРОМа-3.5
+ Документация МУРОМа 4.0
diff --git a/M/doc/ru/keys.html b/M/doc/ru/keys.html
index b15d0c9..cefcc5e 100644
--- a/M/doc/ru/keys.html
+++ b/M/doc/ru/keys.html
@@ -78,12 +78,12 @@
}
- Документация МУРОМа-3.5
+ Документация МУРОМа 4.0
@@ -97,11 +97,13 @@
Ниже представлены страницы с описанием каждого отдельного поддерживаемого ключа
и примерами их использования:
+виды
заголовок
изображения
объекты
слежение
тела
+физика
diff --git a/M/doc/ru/keys.md b/M/doc/ru/keys.md
index a91f1d2..dccc68c 100644
--- a/M/doc/ru/keys.md
+++ b/M/doc/ru/keys.md
@@ -7,14 +7,18 @@ Lang: ru
Ниже представлены страницы с описанием каждого отдельного поддерживаемого ключа
и примерами их использования:
+1. [виды][styles]
1. [заголовок][title]
1. [изображения][images]
1. [объекты][objects]
1. [слежение][tracking]
1. [тела][bodies]
+1. [физика][physics]
[bodies]: bodies.html
[images]: images.html
[objects]: objects.html
+[physics]: physics.html
+[styles]: styles.html
[title]: title.html
[tracking]: tracking.html
diff --git a/M/doc/ru/objects.html b/M/doc/ru/objects.html
index bc79ebb..5985462 100644
--- a/M/doc/ru/objects.html
+++ b/M/doc/ru/objects.html
@@ -78,12 +78,12 @@
}
- Документация МУРОМа-3.5
+ Документация МУРОМа 4.0
@@ -97,7 +97,7 @@
Добавление одного или нескольких объектов, которые представляют собой комбинацию изображения , тела и их синхронизацию через слежение .
Пример № 1. Отображение двух падающих на платформу пауков.
-
+
diff --git a/M/doc/ru/objects.md b/M/doc/ru/objects.md
index de992d6..8296acb 100644
--- a/M/doc/ru/objects.md
+++ b/M/doc/ru/objects.md
@@ -10,7 +10,7 @@ Lang: ru
Пример № 1. Отображение двух падающих на платформу пауков.
-
+
diff --git a/M/doc/ru/physics.html b/M/doc/ru/physics.html
new file mode 100644
index 0000000..6a7727b
--- /dev/null
+++ b/M/doc/ru/physics.html
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+ Документация МУРОМа 4.0
+
+
+
+
+
+
+физика
+
+
+
+
+
Задание одного или нескольких настроек физики Matter.js, которые будут применяться для конкретного тела или объекта .
+
+
Пример № 1. Маски столкновений.
+
+
+
+
+
+
+
+
diff --git a/M/doc/ru/physics.md b/M/doc/ru/physics.md
new file mode 100644
index 0000000..12bba81
--- /dev/null
+++ b/M/doc/ru/physics.md
@@ -0,0 +1,18 @@
+Title: физика
+Date: 2021-10-12 00:00
+Category: Страница
+Slug: physics
+Lang: ru
+
+Задание одного или нескольких настроек физики Matter.js, которые будут применяться для конкретного [тела][bodies] или [объекта][objects].
+
+
+
+Пример № 1. Маски столкновений.
+
+
+
+
+
+[bodies]: bodies.html
+[objects]: objects.html
diff --git a/M/doc/ru/styles.html b/M/doc/ru/styles.html
new file mode 100644
index 0000000..519ee29
--- /dev/null
+++ b/M/doc/ru/styles.html
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+ Документация МУРОМа 4.0
+
+
+
+
+
+
+виды
+
+
+
+
+
Задание одного или нескольких стилей CSS, которые будут применяться для конкретного изображения или объекта .
+
+
Пример № 1. Задание стилей для изображения и объекта.
+
+
+
+
+
+
+
+
diff --git a/M/doc/ru/styles.md b/M/doc/ru/styles.md
new file mode 100644
index 0000000..381087b
--- /dev/null
+++ b/M/doc/ru/styles.md
@@ -0,0 +1,18 @@
+Title: виды
+Date: 2021-10-12 00:00
+Category: Страница
+Slug: styles
+Lang: ru
+
+Задание одного или нескольких стилей CSS, которые будут применяться для конкретного [изображения][images] или [объекта][objects].
+
+
+
+Пример № 1. Задание стилей для изображения и объекта.
+
+
+
+
+
+[images]: images.html
+[objects]: objects.html
diff --git a/M/doc/ru/title.html b/M/doc/ru/title.html
index e6c2f70..4434903 100644
--- a/M/doc/ru/title.html
+++ b/M/doc/ru/title.html
@@ -78,12 +78,12 @@
}
- Документация МУРОМа-3.5
+ Документация МУРОМа 4.0
diff --git a/M/doc/ru/tracking.html b/M/doc/ru/tracking.html
index de7b32e..145ab33 100644
--- a/M/doc/ru/tracking.html
+++ b/M/doc/ru/tracking.html
@@ -78,12 +78,12 @@
}
- Документация МУРОМа-3.5
+ Документация МУРОМа 4.0
@@ -95,12 +95,13 @@
Синхронизация изображения с телом. Исполняется каждый кадр.
+
ВНИМАНИЕ : на текущий момент некорректно работает с составными телами: не учитывается вращение частей.
Пример № 1. Мгновенное следование изображения за телом.
-
+
Пример № 2. Замедленное следование изображения за телом со смещением и пределом для ограничения мельтешения.
-
+
diff --git a/M/doc/ru/tracking.md b/M/doc/ru/tracking.md
index 74e36e0..3729f7a 100644
--- a/M/doc/ru/tracking.md
+++ b/M/doc/ru/tracking.md
@@ -6,17 +6,19 @@ Lang: ru
Синхронизация изображения с телом. Исполняется каждый кадр.
+**ВНИМАНИЕ**: на текущий момент некорректно работает с составными телами: не учитывается вращение частей.
+
Пример № 1. Мгновенное следование изображения за телом.
-
+
Пример № 2. Замедленное следование изображения за телом со смещением и пределом для ограничения мельтешения.
-
+
diff --git a/M/index.html b/M/index.html
index f78163c..f431ed0 100644
--- a/M/index.html
+++ b/M/index.html
@@ -1,4 +1,4 @@
-
+