From 5176c6755d48f73455e60474b980ef2a6d6fe82d 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, 5 Oct 2021 14:47:12 +0300
Subject: [PATCH] M-3.5
---
M/3.5/игра.html | 37 +++++
M/3.5/игра/050.Заголовок.js | 10 ++
M/3.5/игра/100.Изображения.js | 104 ++++++++++++++
M/3.5/игра/120.Физика.js | 19 +++
M/3.5/игра/140.Тела.js | 123 +++++++++++++++++
M/3.5/игра/160.Слежение.js | 97 +++++++++++++
M/3.5/игра/180.Объекты.js | 111 +++++++++++++++
M/3.5/игра/700.Игра.js | 67 +++++++++
M/3.5/игра/matter-js | 1 +
M/3.5/общее/100.События.js | 31 +++++
M/3.5/общее/150.Состояние.js | 61 +++++++++
M/3.5/общее/200.функции.js | 91 +++++++++++++
M/3.5/общее/base64-js | 1 +
M/3.5/общее/pako | 1 +
M/3.5/редактор.html | 106 +++++++++++++++
.../редактор/100.Редактор.js | 127 ++++++++++++++++++
M/3.5/редактор/200.Пульт.js | 53 ++++++++
M/3.5/редактор/700.Муром.js | 13 ++
M/3.5/редактор/ace | 1 +
M/3.5/редактор/uikit | 1 +
M/doc/ru/bodies.html | 4 +-
M/doc/ru/images.html | 4 +-
M/doc/ru/item.template | 4 +-
M/doc/ru/keys.html | 5 +-
M/doc/ru/keys.md | 2 +
M/doc/ru/objects.html | 111 +++++++++++++++
M/doc/ru/objects.md | 19 +++
M/doc/ru/title.html | 4 +-
M/doc/ru/tracking.html | 4 +-
M/index.html | 2 +-
30 files changed, 1201 insertions(+), 13 deletions(-)
create mode 100644 M/3.5/игра.html
create mode 100644 M/3.5/игра/050.Заголовок.js
create mode 100644 M/3.5/игра/100.Изображения.js
create mode 100644 M/3.5/игра/120.Физика.js
create mode 100644 M/3.5/игра/140.Тела.js
create mode 100644 M/3.5/игра/160.Слежение.js
create mode 100644 M/3.5/игра/180.Объекты.js
create mode 100644 M/3.5/игра/700.Игра.js
create mode 120000 M/3.5/игра/matter-js
create mode 100644 M/3.5/общее/100.События.js
create mode 100644 M/3.5/общее/150.Состояние.js
create mode 100644 M/3.5/общее/200.функции.js
create mode 120000 M/3.5/общее/base64-js
create mode 120000 M/3.5/общее/pako
create mode 100644 M/3.5/редактор.html
create mode 100644 M/3.5/редактор/100.Редактор.js
create mode 100644 M/3.5/редактор/200.Пульт.js
create mode 100644 M/3.5/редактор/700.Муром.js
create mode 120000 M/3.5/редактор/ace
create mode 120000 M/3.5/редактор/uikit
create mode 100644 M/doc/ru/objects.html
create mode 100644 M/doc/ru/objects.md
diff --git a/M/3.5/игра.html b/M/3.5/игра.html
new file mode 100644
index 0000000..77ca99c
--- /dev/null
+++ b/M/3.5/игра.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/M/3.5/игра/050.Заголовок.js b/M/3.5/игра/050.Заголовок.js
new file mode 100644
index 0000000..2e47500
--- /dev/null
+++ b/M/3.5/игра/050.Заголовок.js
@@ -0,0 +1,10 @@
+function Заголовок()
+{
+ this.обработатьКлюч = function(ключ, путь, значение)
+ {
+ if (ключ == "заголовок")
+ {
+ document.title = значение;
+ }
+ }
+}
diff --git a/M/3.5/игра/100.Изображения.js b/M/3.5/игра/100.Изображения.js
new file mode 100644
index 0000000..40da647
--- /dev/null
+++ b/M/3.5/игра/100.Изображения.js
@@ -0,0 +1,104 @@
+function Изображения(корень)
+{
+ this.создать = function()
+ {
+ this.умолчание = {
+ x: 0,
+ y: 0,
+ ширина: 100,
+ высота: 100,
+ угол: 0,
+ };
+ this.задано = {};
+ this.элементы = {};
+ };
+
+ this.создатьИлиПолучитьЭлемент = function(имя)
+ {
+ var эл = this.элементы[имя];
+ if (эл)
+ {
+ return эл;
+ }
+
+ var ум = this.умолчание;
+ эл = document.createElement("div");
+ эл.id = `изображения-${имя}`;
+ эл.style.position = "absolute";
+ эл.style.display = "block";
+ // Свойства по умолчанию.
+ эл.style.left = `${ум.x}px`;
+ эл.style.top = `${ум.y}px`;
+ эл.style.width = `${ум.ширина}px`;
+ эл.style.height = `${ум.высота}px`;
+ эл.style.transform = `rotate(${ум.угол}rad)`;
+
+ this.элементы[имя] = эл;
+ корень.appendChild(эл);
+ return эл;
+ };
+
+ this.обновитьЭлемент = function(имя, свойство, значение)
+ {
+ if (!this.задано[имя])
+ {
+ this.задано[имя] = {};
+ }
+ this.задано[имя][свойство] = значение;
+ var за = this.задано[имя];
+ var эл = this.создатьИлиПолучитьЭлемент(имя);
+
+ if (свойство == "ширина")
+ {
+ эл.style.width = `${за.ширина}px`;
+ }
+ else if (свойство == "высота")
+ {
+ эл.style.height = `${за.высота}px`;
+ }
+ else if (
+ (свойство == "x") ||
+ (свойство == "y") ||
+ (свойство == "угол") ||
+ (свойство == "вид.transform")
+ ) {
+ var ум = this.умолчание;
+ var x = за.x ? за.x : ум.x;
+ var y = за.y ? за.y : ум.y;
+ var угол = за.угол ? за.угол : ум.угол;
+ this.обновитьРасположение(имя, x, y, угол);
+ }
+ else if (свойство.startsWith("вид"))
+ {
+ var параметр = свойство.substring(4);
+ эл.style.setProperty(параметр, значение);
+ }
+ };
+
+ this.обновитьРасположение = function(имя, x, y, угол)
+ {
+ var за = this.задано[имя];
+ var эл = this.элементы[имя];
+ эл.style.left = `${x}px`;
+ эл.style.top = `${y}px`;
+ var transform = `rotate(${угол}deg) `;
+ if (за["вид.transform"])
+ {
+ transform += за["вид.transform"];
+ }
+ эл.style.transform = transform;
+ };
+
+ this.обработатьКлюч = function(ключ, путь, значение)
+ {
+ if (путь[0] == "изображения")
+ {
+ var имя = путь[1];
+ var свойство = путь.slice(2).join(".");
+ this.обновитьЭлемент(имя, свойство, значение);
+ }
+ };
+
+ // Конструктор.
+ this.создать();
+}
diff --git a/M/3.5/игра/120.Физика.js b/M/3.5/игра/120.Физика.js
new file mode 100644
index 0000000..4f8ccbb
--- /dev/null
+++ b/M/3.5/игра/120.Физика.js
@@ -0,0 +1,19 @@
+function Физика()
+{
+ this.создать = function()
+ {
+ this.движок = Matter.Engine.create({
+ enableSleeping: true,
+ gravity: {
+ y: 2,
+ },
+ });
+ };
+
+ this.обновить = function() {
+ Matter.Engine.update(this.движок);
+ };
+
+ // Конструктор.
+ this.создать();
+}
diff --git a/M/3.5/игра/140.Тела.js b/M/3.5/игра/140.Тела.js
new file mode 100644
index 0000000..43621bf
--- /dev/null
+++ b/M/3.5/игра/140.Тела.js
@@ -0,0 +1,123 @@
+function Тела(события, физмир)
+{
+ this.создать = function()
+ {
+ this.умолчание = {
+ x: 0,
+ y: 0,
+ ширина: 40,
+ высота: 20,
+ часть: false,
+ физика: {
+ isStatic: true,
+ },
+ };
+
+ 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(имя) {
+ // Удаляем старое тело.
+ if (имя in this.тела)
+ {
+ var тело = this.тела[имя];
+ delete this.имена[тело.id];
+ // Всегда удаляем из мира: и составные, и несоставные тела.
+ Matter.Composite.remove(физмир, тело);
+ }
+
+ var за = this.задано[имя];
+ var ум = this.умолчание;
+ var x = (за.x != null) ? за.x : ум.x;
+ var y = (за.y != null) ? за.y : ум.y;
+ var ширина = за.ширина ? за.ширина : ум.ширина;
+ var высота = за.высота ? за.высота : ум.высота;
+ // Переводим x,y из левого верхнего угла в центр.
+ x = x + ширина / 2.0;
+ y = y + высота / 2.0;
+ // Параметры тела.
+ var параметры = {};
+ Object.assign(параметры, ум.физика);
+ for (var путь in за)
+ {
+ if (путь.startsWith("физика"))
+ {
+ var свойство = путь.slice(7);
+ мир.задатьПолныйКлюч(параметры, свойство, за[путь]);
+ }
+ }
+
+ var тело = null;
+ // Создаём новое составное тело.
+ if (за["части.0"])
+ {
+ параметры["parts"] = this.телаЧастей(this.именаЧастей(за));
+ тело = Matter.Body.create(параметры);
+ }
+ // Создаём новое несоставное тело.
+ // Вполне может быть частью другого составного тела.
+ else
+ {
+ тело = Matter.Bodies.rectangle(x, y, ширина, высота, параметры);
+ }
+ this.тела[имя] = тело;
+ this.имена[тело.id] = имя;
+
+ // Добавляем тело в физический мир, если оно не является частью другого составного тела.
+ var часть = за.часть ? за.часть : ум.часть;
+ if (!часть)
+ {
+ Matter.Composite.add(физмир, тело);
+ }
+ };
+
+ this.телаЧастей = function(имена)
+ {
+ var тела = [];
+ for (var н in имена)
+ {
+ var имя = имена[н];
+ var тело = this.тела[имя];
+ тела.push(тело);
+ }
+ return тела;
+ };
+
+ // Конструктор.
+ this.создать();
+}
+
diff --git a/M/3.5/игра/160.Слежение.js b/M/3.5/игра/160.Слежение.js
new file mode 100644
index 0000000..2c7de17
--- /dev/null
+++ b/M/3.5/игра/160.Слежение.js
@@ -0,0 +1,97 @@
+function Слежение(события, изображения, тела)
+{
+ this.создать = function()
+ {
+ this.умолчание = {
+ смещение: [0, 0],
+ скорость: 1,
+ предел: 0,
+ };
+ this.задано = {};
+ события.подписать(this);
+ };
+
+ this.обновить = function()
+ {
+ for (var имя in this.задано)
+ {
+ var за = this.задано[имя];
+ this.расположитьИзображение(за);
+ }
+ };
+
+ this.обработатьКлюч = function(ключ, путь, значение)
+ {
+ if (путь[0] != "слежение")
+ {
+ return;
+ }
+
+ var имя = путь[1];
+ var свойство = путь.slice(2).join(".");
+ if (!this.задано[имя])
+ {
+ this.задано[имя] = {};
+ }
+ this.задано[имя][свойство] = значение;
+ };
+
+ this.расположитьИзображение = function(за)
+ {
+ var тело = тела.тела[за.тело];
+ if (!тело)
+ {
+ return;
+ }
+
+ var элемент = изображения.элементы[за.изображение];
+ var ум = 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;
+ изображения.обновитьРасположение(за.изображение, 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/3.5/игра/180.Объекты.js b/M/3.5/игра/180.Объекты.js
new file mode 100644
index 0000000..546482a
--- /dev/null
+++ b/M/3.5/игра/180.Объекты.js
@@ -0,0 +1,111 @@
+function Объекты(события, состояние)
+{
+ this.создать = function()
+ {
+ this.умолчание = {
+ пр: [0, 0, 40, 20],
+ };
+ this.задано = {};
+ события.подписать(this);
+ };
+
+ this.обновитьИзображение = function(имя)
+ {
+ let имяИзо = `объекты-${имя}`;
+ let за = this.задано[имя];
+ let пр = this.пр(имя);
+ var описание = {
+ ширина: пр[2],
+ высота: пр[3],
+ вид: {},
+ };
+ for (var ключ in за)
+ {
+ if (ключ.startsWith("вид."))
+ {
+ var к = ключ.substring(4);
+ var значение = за[ключ];
+ описание.вид[к] = значение;
+ }
+ }
+
+ состояние.разобрать({
+ изображения: {
+ [имяИзо]: описание,
+ },
+ });
+ };
+
+ this.обновитьСлежение = function(имя)
+ {
+ let имяС = `объекты-${имя}`;
+ let пр = this.пр(имя);
+ состояние.разобрать({
+ слежение: {
+ [имяС]: {
+ изображение: имяС,
+ тело: имяС,
+ смещение: [-пр[2] / 2, -пр[3] / 2],
+ },
+ },
+ });
+ };
+
+ this.обработатьКлюч = function(ключ, путь, значение)
+ {
+ if (путь[0] != "объекты")
+ {
+ return;
+ }
+
+ let имя = путь[1];
+ let свойство = путь.slice(2).join(".");
+ if (!this.задано[имя])
+ {
+ this.задано[имя] = {};
+ }
+ this.задано[имя][свойство] = значение;
+
+ let пр = свойство.startsWith("пр.");
+ if (пр)
+ {
+ this.пересоздатьТело(имя);
+ this.обновитьСлежение(имя);
+ }
+ this.обновитьИзображение(имя);
+ };
+
+ this.пересоздатьТело = function(имя)
+ {
+ let имяТела = `объекты-${имя}`;
+ let пр = this.пр(имя);
+ состояние.разобрать({
+ тела: {
+ [имяТела]: {
+ x: пр[0],
+ y: пр[1],
+ ширина: пр[2],
+ высота: пр[3],
+ физика: {
+ isStatic: false,
+ },
+ },
+ },
+ });
+ };
+
+ this.пр = function(имя)
+ {
+ let за = this.задано[имя];
+ let пр = this.умолчание.пр;
+ return [
+ за["пр.0"] ? за["пр.0"] : пр[0],
+ за["пр.1"] ? за["пр.1"] : пр[1],
+ за["пр.2"] ? за["пр.2"] : пр[2],
+ за["пр.3"] ? за["пр.3"] : пр[3],
+ ];
+ };
+
+ // Конструктор.
+ this.создать();
+};
diff --git a/M/3.5/игра/700.Игра.js b/M/3.5/игра/700.Игра.js
new file mode 100644
index 0000000..82d721d
--- /dev/null
+++ b/M/3.5/игра/700.Игра.js
@@ -0,0 +1,67 @@
+function Игра()
+{
+ this.создать = function()
+ {
+ var корень = document.getElementById("корень");
+ мир.события = new События();
+ мир.состояние = new Состояние();
+ мир.состояние.обработчик = (к, п, з) => { this.обработатьКлюч(к, п, з) };
+ мир.физика = 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()
+ {
+ мир.физика.обновить();
+ мир.слежение.обновить();
+
+ 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/3.5/игра/matter-js b/M/3.5/игра/matter-js
new file mode 120000
index 0000000..3a19a2e
--- /dev/null
+++ b/M/3.5/игра/matter-js
@@ -0,0 +1 @@
+../../стороннее/matter-js
\ No newline at end of file
diff --git a/M/3.5/общее/100.События.js b/M/3.5/общее/100.События.js
new file mode 100644
index 0000000..19f8ecf
--- /dev/null
+++ b/M/3.5/общее/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/3.5/общее/150.Состояние.js b/M/3.5/общее/150.Состояние.js
new file mode 100644
index 0000000..5004890
--- /dev/null
+++ b/M/3.5/общее/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/3.5/общее/200.функции.js b/M/3.5/общее/200.функции.js
new file mode 100644
index 0000000..cca058c
--- /dev/null
+++ b/M/3.5/общее/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/3.5/общее/base64-js b/M/3.5/общее/base64-js
new file mode 120000
index 0000000..a3367c1
--- /dev/null
+++ b/M/3.5/общее/base64-js
@@ -0,0 +1 @@
+../../стороннее/base64-js
\ No newline at end of file
diff --git a/M/3.5/общее/pako b/M/3.5/общее/pako
new file mode 120000
index 0000000..504d162
--- /dev/null
+++ b/M/3.5/общее/pako
@@ -0,0 +1 @@
+../../стороннее/pako
\ No newline at end of file
diff --git a/M/3.5/редактор.html b/M/3.5/редактор.html
new file mode 100644
index 0000000..e027c35
--- /dev/null
+++ b/M/3.5/редактор.html
@@ -0,0 +1,106 @@
+
+
+
+
+ M-3.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3.5
+ объекты
+
+
+ 3.4
+ Документация
+
+
+ 3.3
+ тела, слежение
+
+
+ 3.2
+ мир.параметрыЗапуска, мир.разобрать, заголовок, изображения, состояние, физика
+
+
+ 3.1
+ Кнопки перезапуска, копирования ссылки, запуска в отдельной вкладке
+
+
+ 3.0
+ Проверка введённого кода на отсутствие синтаксических ошибок
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/M/3.5/редактор/100.Редактор.js b/M/3.5/редактор/100.Редактор.js
new file mode 100644
index 0000000..430d3a8
--- /dev/null
+++ b/M/3.5/редактор/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/3.5/редактор/200.Пульт.js b/M/3.5/редактор/200.Пульт.js
new file mode 100644
index 0000000..42889e5
--- /dev/null
+++ b/M/3.5/редактор/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/3.5/редактор/700.Муром.js b/M/3.5/редактор/700.Муром.js
new file mode 100644
index 0000000..fa45750
--- /dev/null
+++ b/M/3.5/редактор/700.Муром.js
@@ -0,0 +1,13 @@
+function Муром()
+{
+ this.создать = function()
+ {
+ мир.события = new События();
+ мир.состояние = new Состояние();
+ мир.редактор = new Редактор(мир.события, "редактор");
+ мир.пульт = new Пульт(мир.события, "ошибка", "перезапустить", "отдельно", "ссылка", "документация");
+ };
+
+ // Конструктор.
+ this.создать();
+}
diff --git a/M/3.5/редактор/ace b/M/3.5/редактор/ace
new file mode 120000
index 0000000..0190be9
--- /dev/null
+++ b/M/3.5/редактор/ace
@@ -0,0 +1 @@
+../../стороннее/ace
\ No newline at end of file
diff --git a/M/3.5/редактор/uikit b/M/3.5/редактор/uikit
new file mode 120000
index 0000000..d60f2bc
--- /dev/null
+++ b/M/3.5/редактор/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 35194bb..03fab74 100644
--- a/M/doc/ru/bodies.html
+++ b/M/doc/ru/bodies.html
@@ -78,12 +78,12 @@
}
- Документация МУРОМа
+ Документация МУРОМа-3.5
diff --git a/M/doc/ru/images.html b/M/doc/ru/images.html
index 1b931a9..c63729d 100644
--- a/M/doc/ru/images.html
+++ b/M/doc/ru/images.html
@@ -78,12 +78,12 @@
}
- Документация МУРОМа
+ Документация МУРОМа-3.5
diff --git a/M/doc/ru/item.template b/M/doc/ru/item.template
index 452af00..12e9971 100644
--- a/M/doc/ru/item.template
+++ b/M/doc/ru/item.template
@@ -78,12 +78,12 @@
}
- Документация МУРОМа
+ Документация МУРОМа-3.5
diff --git a/M/doc/ru/keys.html b/M/doc/ru/keys.html
index 87dc859..b15d0c9 100644
--- a/M/doc/ru/keys.html
+++ b/M/doc/ru/keys.html
@@ -78,12 +78,12 @@
}
- Документация МУРОМа
+ Документация МУРОМа-3.5
@@ -99,6 +99,7 @@
заголовок
изображения
+объекты
слежение
тела
diff --git a/M/doc/ru/keys.md b/M/doc/ru/keys.md
index 1742e98..a91f1d2 100644
--- a/M/doc/ru/keys.md
+++ b/M/doc/ru/keys.md
@@ -9,10 +9,12 @@ Lang: ru
1. [заголовок][title]
1. [изображения][images]
+1. [объекты][objects]
1. [слежение][tracking]
1. [тела][bodies]
[bodies]: bodies.html
[images]: images.html
+[objects]: objects.html
[title]: title.html
[tracking]: tracking.html
diff --git a/M/doc/ru/objects.html b/M/doc/ru/objects.html
new file mode 100644
index 0000000..bc79ebb
--- /dev/null
+++ b/M/doc/ru/objects.html
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+ Документация МУРОМа-3.5
+
+
+
+
+
+
+объекты
+
+
+
+
+
Добавление одного или нескольких объектов, которые представляют собой комбинацию изображения , тела и их синхронизацию через слежение .
+
+
Пример № 1. Отображение двух падающих на платформу пауков.
+
+
+
+
+
+
+
+
diff --git a/M/doc/ru/objects.md b/M/doc/ru/objects.md
new file mode 100644
index 0000000..de992d6
--- /dev/null
+++ b/M/doc/ru/objects.md
@@ -0,0 +1,19 @@
+Title: объекты
+Date: 2021-10-05 00:00
+Category: Страница
+Slug: objects
+Lang: ru
+
+Добавление одного или нескольких объектов, которые представляют собой комбинацию [изображения][images], [тела][bodies] и их синхронизацию через [слежение][tracking].
+
+
+
+Пример № 1. Отображение двух падающих на платформу пауков.
+
+
+
+
+
+[bodies]: bodies.html
+[images]: images.html
+[tracking]: tracking.html
diff --git a/M/doc/ru/title.html b/M/doc/ru/title.html
index 1ca950e..e6c2f70 100644
--- a/M/doc/ru/title.html
+++ b/M/doc/ru/title.html
@@ -78,12 +78,12 @@
}
- Документация МУРОМа
+ Документация МУРОМа-3.5
diff --git a/M/doc/ru/tracking.html b/M/doc/ru/tracking.html
index ff3dc31..de7b32e 100644
--- a/M/doc/ru/tracking.html
+++ b/M/doc/ru/tracking.html
@@ -78,12 +78,12 @@
}
- Документация МУРОМа
+ Документация МУРОМа-3.5
diff --git a/M/index.html b/M/index.html
index 754a3bc..f78163c 100644
--- a/M/index.html
+++ b/M/index.html
@@ -1,4 +1,4 @@
-
+