<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
	<id>https://wiki.murs-mc.ru/w/index.php?action=history&amp;feed=atom&amp;title=MURS%3A%D0%A1%D0%BF%D0%B5%D1%86%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D1%8B_%D0%B4%D0%BB%D1%8F_%D0%BE%D1%81%D0%BE%D0%B1%D1%8B%D1%85_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87</id>
	<title>MURS:Специальные скрипты для особых задач - История изменений</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.murs-mc.ru/w/index.php?action=history&amp;feed=atom&amp;title=MURS%3A%D0%A1%D0%BF%D0%B5%D1%86%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D1%8B_%D0%B4%D0%BB%D1%8F_%D0%BE%D1%81%D0%BE%D0%B1%D1%8B%D1%85_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87"/>
	<link rel="alternate" type="text/html" href="https://wiki.murs-mc.ru/w/index.php?title=MURS:%D0%A1%D0%BF%D0%B5%D1%86%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D1%8B_%D0%B4%D0%BB%D1%8F_%D0%BE%D1%81%D0%BE%D0%B1%D1%8B%D1%85_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87&amp;action=history"/>
	<updated>2026-07-27T07:26:41Z</updated>
	<subtitle>История изменений этой страницы в вики</subtitle>
	<generator>MediaWiki 1.45.3</generator>
	<entry>
		<id>https://wiki.murs-mc.ru/w/index.php?title=MURS:%D0%A1%D0%BF%D0%B5%D1%86%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D1%8B_%D0%B4%D0%BB%D1%8F_%D0%BE%D1%81%D0%BE%D0%B1%D1%8B%D1%85_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87&amp;diff=2723&amp;oldid=prev</id>
		<title>Tringoli в 18:53, 26 июля 2026</title>
		<link rel="alternate" type="text/html" href="https://wiki.murs-mc.ru/w/index.php?title=MURS:%D0%A1%D0%BF%D0%B5%D1%86%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D1%8B_%D0%B4%D0%BB%D1%8F_%D0%BE%D1%81%D0%BE%D0%B1%D1%8B%D1%85_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87&amp;diff=2723&amp;oldid=prev"/>
		<updated>2026-07-26T18:53:19Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;ru&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Предыдущая версия&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Версия от 18:53, 26 июля 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l24&quot;&gt;Строка 24:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Строка 24:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;# Переместитесь на нужный участок застройки и нажмите горячую клавишу.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;# Переместитесь на нужный участок застройки и нажмите горячую клавишу.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Пример: &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Автоматизация &lt;/del&gt;освещения &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;окон &lt;/del&gt;==&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Пример: &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;автоматизация расстановки &lt;/ins&gt;освещения &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;внутри зданий &lt;/ins&gt;==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;В Муроморске насчитываются сотни многоэтажных жилых домов и промышленных зданий с тысячами оконных проёмов. Ручная расстановка блоков света внутри каждого окна занимает десятки часов, а массовое копирование командой &amp;lt;code&amp;gt;//copy&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;//paste&amp;lt;/code&amp;gt; создаёт неестественный повтор рисунка светящихся окон.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;В Муроморске насчитываются сотни многоэтажных жилых домов и промышленных зданий с тысячами оконных проёмов. Ручная расстановка блоков света внутри каждого окна занимает десятки часов, а массовое копирование командой &amp;lt;code&amp;gt;//copy&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;//paste&amp;lt;/code&amp;gt; создаёт неестественный повтор рисунка светящихся окон.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;

&lt;!-- diff cache key mediawiki:diff:1.41:old-2722:rev-2723:php=table --&gt;
&lt;/table&gt;</summary>
		<author><name>Tringoli</name></author>
	</entry>
	<entry>
		<id>https://wiki.murs-mc.ru/w/index.php?title=MURS:%D0%A1%D0%BF%D0%B5%D1%86%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D1%8B_%D0%B4%D0%BB%D1%8F_%D0%BE%D1%81%D0%BE%D0%B1%D1%8B%D1%85_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87&amp;diff=2722&amp;oldid=prev</id>
		<title>Tringoli: Новая страница: «← Вернуться к разделу «MURS:Строителям»  &#039;&#039;&#039;Специальные скрипты для особых задач&#039;&#039;&#039; — подборка клиентских и серверных автоматизированных сценариев на базе JavaScript для ускорения рутинных процессов строительства в Муроморске и [...»</title>
		<link rel="alternate" type="text/html" href="https://wiki.murs-mc.ru/w/index.php?title=MURS:%D0%A1%D0%BF%D0%B5%D1%86%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D1%8B_%D0%B4%D0%BB%D1%8F_%D0%BE%D1%81%D0%BE%D0%B1%D1%8B%D1%85_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87&amp;diff=2722&amp;oldid=prev"/>
		<updated>2026-07-26T18:52:03Z</updated>

		<summary type="html">&lt;p&gt;Новая страница: «&lt;a href=&quot;/wiki/MURS:%D0%A1%D1%82%D1%80%D0%BE%D0%B8%D1%82%D0%B5%D0%BB%D1%8F%D0%BC&quot; title=&quot;MURS:Строителям&quot;&gt;← Вернуться к разделу «MURS:Строителям»&lt;/a&gt;  &amp;#039;&amp;#039;&amp;#039;Специальные скрипты для особых задач&amp;#039;&amp;#039;&amp;#039; — подборка клиентских и серверных автоматизированных сценариев на базе JavaScript для ускорения рутинных процессов строительства в &lt;a href=&quot;/wiki/%D0%9C%D1%83%D1%80%D0%BE%D0%BC%D0%BE%D1%80%D1%81%D0%BA&quot; title=&quot;Муроморск&quot;&gt;Муроморске&lt;/a&gt; и [...»&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая страница&lt;/b&gt;&lt;/p&gt;&lt;div&gt;[[MURS:Строителям|← Вернуться к разделу «MURS:Строителям»]]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Специальные скрипты для особых задач&amp;#039;&amp;#039;&amp;#039; — подборка клиентских и серверных автоматизированных сценариев на базе JavaScript для ускорения рутинных процессов строительства в [[Муроморск|Муроморске]] и [[Муроморская область|Муроморской области]].&lt;br /&gt;
&lt;br /&gt;
Иногда возникают ситуации, когда возможностей стандартизированных инструментов вроде WorldEdit или Axiom не хватает для решения специфических задач. Например:&lt;br /&gt;
* Автоматическая высадка сложного случайного распределения растительности и деревьев;&lt;br /&gt;
* Масштабное и затяжное терраформирование со сложными масками;&lt;br /&gt;
* Проработка интерьеров и освещения в фасадах тысяч окон спальных районов.&lt;br /&gt;
&lt;br /&gt;
В таких случаях на помощь приходят индивидуальные клиентские скрипты, выполняемые на стороне игрока и не требующие установки дополнительных плагинов на сервер.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Быстрый алгоритм настройки ==&lt;br /&gt;
&lt;br /&gt;
Для запуска клиентских скриптов используется модификация &amp;#039;&amp;#039;&amp;#039;JsMacros&amp;#039;&amp;#039;&amp;#039; (или аналогичные моды на CraftScripts для WorldEdit).&lt;br /&gt;
&lt;br /&gt;
# Скачайте и установите мод JsMacros в сборку (совместимую с версией 1.19.4).&lt;br /&gt;
# Подготовьте или напишите готовый файл скрипта с расширением `.js`.&lt;br /&gt;
# Поместите файл в папку сборки:&lt;br /&gt;
#: &amp;lt;code&amp;gt;.minecraft/config/jsMacros/Macros/&amp;lt;/code&amp;gt;&lt;br /&gt;
# Откройте меню мода в игре (по умолчанию клавиша &amp;#039;&amp;#039;G&amp;#039;&amp;#039; или через настройки управления).&lt;br /&gt;
# Назначьте вызов нужного скрипта на удобную горячую клавишу (Keybinding) или событие.&lt;br /&gt;
# Переместитесь на нужный участок застройки и нажмите горячую клавишу.&lt;br /&gt;
&lt;br /&gt;
== Пример: Автоматизация освещения окон ==&lt;br /&gt;
&lt;br /&gt;
В Муроморске насчитываются сотни многоэтажных жилых домов и промышленных зданий с тысячами оконных проёмов. Ручная расстановка блоков света внутри каждого окна занимает десятки часов, а массовое копирование командой &amp;lt;code&amp;gt;//copy&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;//paste&amp;lt;/code&amp;gt; создаёт неестественный повтор рисунка светящихся окон.&lt;br /&gt;
&lt;br /&gt;
Ниже представлен пример скрипта от &amp;#039;&amp;#039;&amp;#039;[[Tringoli]]&amp;#039;&amp;#039;&amp;#039; для автоматической случайной расстановки блоков света и темноты за блоками стёкол. Он применяется &amp;#039;&amp;#039;&amp;#039;снаружи&amp;#039;&amp;#039;&amp;#039; здания.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;border: 1px solid #a2a9b1; background-color: #f8f9fa; padding: 10px; border-radius: 6px; margin-bottom: 12px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;📖 Код скрипта&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot; style=&amp;quot;margin-top: 8px;&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// Умное освещение окон с 3D-анализом плотности интерьера (JS Macros 1.19.4)&lt;br /&gt;
const radius = 15;        // Радиус сканирования вокруг игрока&lt;br /&gt;
const lightRatio = 0.3;   // 30% свет, 70% тьма&lt;br /&gt;
const blocksPerTick = 4;  // Ускорение: 4 блока за 1 тик (80 блоков/сек)&lt;br /&gt;
&lt;br /&gt;
// Идентификаторы блоков света и тьмы&lt;br /&gt;
const blockLight = &amp;quot;chisel:large_tile/glowstone&amp;quot;;&lt;br /&gt;
const blockDark = &amp;quot;minecraft:black_concrete&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const player = Player.getPlayer();&lt;br /&gt;
&lt;br /&gt;
// Главный вектор взгляда игрока вглубь фасада&lt;br /&gt;
function getPlayerInwardVector() {&lt;br /&gt;
    let yaw = player.getYaw() % 360;&lt;br /&gt;
    if (yaw &amp;lt; 0) yaw += 360;&lt;br /&gt;
    &lt;br /&gt;
    if (yaw &amp;gt;= 315 || yaw &amp;lt; 45) return { dx: 0, dz: 1 };   // Взгляд на Юг -&amp;gt; комната на Юге (+Z)&lt;br /&gt;
    if (yaw &amp;gt;= 45 &amp;amp;&amp;amp; yaw &amp;lt; 135) return { dx: -1, dz: 0 };  // Взгляд на Запад -&amp;gt; комната на Западе (-X)&lt;br /&gt;
    if (yaw &amp;gt;= 135 &amp;amp;&amp;amp; yaw &amp;lt; 225) return { dx: 0, dz: -1 }; // Взгляд на Север -&amp;gt; комната на Севере (-Z)&lt;br /&gt;
    if (yaw &amp;gt;= 225 &amp;amp;&amp;amp; yaw &amp;lt; 315) return { dx: 1, dz: 0 };  // Взгляд на Восток -&amp;gt; комната на Востоке (+X)&lt;br /&gt;
    return { dx: 0, dz: -1 };&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
const mainInward = getPlayerInwardVector();&lt;br /&gt;
&lt;br /&gt;
let yawRad = (player.getYaw() % 360) * Math.PI / 180;&lt;br /&gt;
let lookX = -Math.sin(yawRad);&lt;br /&gt;
let lookZ = Math.cos(yawRad);&lt;br /&gt;
&lt;br /&gt;
// Проверка: находится ли окно перед игроком (чтобы не застраивать спину)&lt;br /&gt;
function isInFront(x, z, px, pz) {&lt;br /&gt;
    let dirX = x - px;&lt;br /&gt;
    let dirZ = z - pz;&lt;br /&gt;
    if (dirX === 0 &amp;amp;&amp;amp; dirZ === 0) return true;&lt;br /&gt;
    let dot = dirX * lookX + dirZ * lookZ;&lt;br /&gt;
    return dot &amp;gt; -1.5; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Проверка: является ли блок окном&lt;br /&gt;
function isWindow(id) {&lt;br /&gt;
    let s = String(id).toLowerCase();&lt;br /&gt;
    &lt;br /&gt;
    // 🚫 Исключаем парапеты, перила и внешние ограждения&lt;br /&gt;
    if (s.includes(&amp;quot;parapet&amp;quot;) || s.includes(&amp;quot;railing&amp;quot;) || s.includes(&amp;quot;balcony_railing&amp;quot;)) {&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return s.match(/yuushya:.*(window|glass|diagonal).*/) || &lt;br /&gt;
           s.match(/chipped:.*(glass|pane|window).*/) || &lt;br /&gt;
           s.match(/mcwwindows:.*(window|glass|pane).*/) || &lt;br /&gt;
           s.match(/chisel:.*glass.*/) || &lt;br /&gt;
           s.match(/minecraft:.*glass.*/) ||&lt;br /&gt;
           s.match(/.*:.*(window|stained_glass|glass_pane).*/);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Проверка: является ли элемент подоконником/нижней частью&lt;br /&gt;
function isBottomPart(id) {&lt;br /&gt;
    let s = String(id).toLowerCase();&lt;br /&gt;
    return s.includes(&amp;quot;_bottom&amp;quot;) || &lt;br /&gt;
           s.includes(&amp;quot;window_bottom&amp;quot;) || &lt;br /&gt;
           s.includes(&amp;quot;sill&amp;quot;) || &lt;br /&gt;
           s.includes(&amp;quot;_lower&amp;quot;) ||&lt;br /&gt;
           s.includes(&amp;quot;frame_bottom&amp;quot;) ||&lt;br /&gt;
           s.includes(&amp;quot;parapet&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// 🧠 УЛЬТИМАТИВНЫЙ ПОИСК ТОЧКИ ИНТЕРЬЕРА ЧЕРЕЗ АНАЛИЗ 3D-ОКРУЖЕНИЯ&lt;br /&gt;
function getBestInteriorTarget(x, y, z, px, pz) {&lt;br /&gt;
    let candidates = [&lt;br /&gt;
        { dx: mainInward.dx, dz: mainInward.dz }, // Прямо вглубь по взгляду&lt;br /&gt;
        { dx: 1, dz: 0 },&lt;br /&gt;
        { dx: -1, dz: 0 },&lt;br /&gt;
        { dx: 0, dz: 1 },&lt;br /&gt;
        { dx: 0, dz: -1 },&lt;br /&gt;
        { dx: 1, dz: 1 },&lt;br /&gt;
        { dx: -1, dz: 1 },&lt;br /&gt;
        { dx: 1, dz: -1 },&lt;br /&gt;
        { dx: -1, dz: -1 }&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    let winDist = Math.hypot(x - px, z - pz);&lt;br /&gt;
    let bestTarget = null;&lt;br /&gt;
    let maxScore = -9999;&lt;br /&gt;
&lt;br /&gt;
    for (let i = 0; i &amp;lt; candidates.length; i++) {&lt;br /&gt;
        let c = candidates[i];&lt;br /&gt;
        let tx = x + c.dx;&lt;br /&gt;
        let ty = y;&lt;br /&gt;
        let tz = z + c.dz;&lt;br /&gt;
&lt;br /&gt;
        let bId = World.getBlock(tx, ty, tz).getId();&lt;br /&gt;
        &lt;br /&gt;
        // Точка должна быть воздухом или уже стоящим блоком света/тьмы&lt;br /&gt;
        if (bId !== &amp;quot;minecraft:air&amp;quot; &amp;amp;&amp;amp; bId !== &amp;quot;minecraft:cave_air&amp;quot; &amp;amp;&amp;amp; bId !== blockLight &amp;amp;&amp;amp; bId !== blockDark) {&lt;br /&gt;
            continue;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // 1. Оценка плотности окружающих конструкций (26 соседних блоков)&lt;br /&gt;
        let density = 0;&lt;br /&gt;
        for (let ox = -1; ox &amp;lt;= 1; ox++) {&lt;br /&gt;
            for (let oy = -1; oy &amp;lt;= 1; oy++) {&lt;br /&gt;
                for (let oz = -1; oz &amp;lt;= 1; oz++) {&lt;br /&gt;
                    if (ox === 0 &amp;amp;&amp;amp; oy === 0 &amp;amp;&amp;amp; oz === 0) continue;&lt;br /&gt;
                    let nid = World.getBlock(tx + ox, ty + oy, tz + oz).getId();&lt;br /&gt;
                    if (nid !== &amp;quot;minecraft:air&amp;quot; &amp;amp;&amp;amp; nid !== &amp;quot;minecraft:cave_air&amp;quot;) {&lt;br /&gt;
                        density++;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // 2. Направление относительно взгляда игрока&lt;br /&gt;
        let dotPlayer = c.dx * mainInward.dx + c.dz * mainInward.dz;&lt;br /&gt;
        let targetDist = Math.hypot(tx - px, tz - pz);&lt;br /&gt;
&lt;br /&gt;
        // Расчет итогового рейтинга интерьера&lt;br /&gt;
        let score = density;&lt;br /&gt;
        if (dotPlayer &amp;gt; 0) score += 5;&lt;br /&gt;
        if (dotPlayer &amp;lt; 0) score -= 15; // Жесткий штраф за вылет на улицу перед игроком&lt;br /&gt;
        if (targetDist &amp;gt; winDist) score += 3;&lt;br /&gt;
&lt;br /&gt;
        if (score &amp;gt; maxScore) {&lt;br /&gt;
            maxScore = score;&lt;br /&gt;
            bestTarget = { x: tx, y: ty, z: tz };&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Если точная точка найдена&lt;br /&gt;
    if (bestTarget) {&lt;br /&gt;
        return bestTarget;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Запасной вариант строго по вектору помещения&lt;br /&gt;
    return { x: x + mainInward.dx, y: y, z: z + mainInward.dz };&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
let px = Math.floor(player.getX());&lt;br /&gt;
let py = Math.floor(player.getY());&lt;br /&gt;
let pz = Math.floor(player.getZ());&lt;br /&gt;
&lt;br /&gt;
let visited = new Set();&lt;br /&gt;
let groupsToBuild = [];&lt;br /&gt;
&lt;br /&gt;
function getPosKey(x, y, z) {&lt;br /&gt;
    return `${x},${y},${z}`;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Chat.log(`§e[Урбанист] §fСканирую фасад (3D-анализ интерьера эркеров и балконов)...`);&lt;br /&gt;
&lt;br /&gt;
// 1. Поиск и группировка окон (Flood Fill)&lt;br /&gt;
for (let x = px - radius; x &amp;lt;= px + radius; x++) {&lt;br /&gt;
    for (let y = py - radius; y &amp;lt;= py + radius; y++) {&lt;br /&gt;
        for (let z = pz - radius; z &amp;lt;= pz + radius; z++) {&lt;br /&gt;
            &lt;br /&gt;
            let key = getPosKey(x, y, z);&lt;br /&gt;
            if (visited.has(key)) continue;&lt;br /&gt;
&lt;br /&gt;
            let blockId = World.getBlock(x, y, z).getId();&lt;br /&gt;
            &lt;br /&gt;
            if (isWindow(blockId)) {&lt;br /&gt;
                if (!isInFront(x, z, px, pz)) {&lt;br /&gt;
                    visited.add(key);&lt;br /&gt;
                    continue;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                let stack = [{x: x, y: y, z: z}];&lt;br /&gt;
                visited.add(key);&lt;br /&gt;
&lt;br /&gt;
                let emptyTargets = [];&lt;br /&gt;
                let existingBlock = null;&lt;br /&gt;
&lt;br /&gt;
                while (stack.length &amp;gt; 0) {&lt;br /&gt;
                    let curr = stack.pop();&lt;br /&gt;
                    let currId = World.getBlock(curr.x, curr.y, curr.z).getId();&lt;br /&gt;
&lt;br /&gt;
                    if (!isBottomPart(currId)) {&lt;br /&gt;
                        let target = getBestInteriorTarget(curr.x, curr.y, curr.z, px, pz);&lt;br /&gt;
                        let behindId = World.getBlock(target.x, target.y, target.z).getId();&lt;br /&gt;
&lt;br /&gt;
                        if (behindId === blockLight || behindId === blockDark) {&lt;br /&gt;
                            existingBlock = behindId;&lt;br /&gt;
                        } else if (behindId === &amp;quot;minecraft:air&amp;quot; || behindId === &amp;quot;minecraft:cave_air&amp;quot;) {&lt;br /&gt;
                            emptyTargets.push({ targetX: target.x, targetY: target.y, targetZ: target.z });&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
&lt;br /&gt;
                    let neighbors = [&lt;br /&gt;
                        {x: curr.x + 1, y: curr.y, z: curr.z},&lt;br /&gt;
                        {x: curr.x - 1, y: curr.y, z: curr.z},&lt;br /&gt;
                        {x: curr.x, y: curr.y + 1, z: curr.z},&lt;br /&gt;
                        {x: curr.x, y: curr.y - 1, z: curr.z},&lt;br /&gt;
                        {x: curr.x, y: curr.y, z: curr.z + 1},&lt;br /&gt;
                        {x: curr.x, y: curr.y, z: curr.z - 1}&lt;br /&gt;
                    ];&lt;br /&gt;
&lt;br /&gt;
                    for (let i = 0; i &amp;lt; neighbors.length; i++) {&lt;br /&gt;
                        let n = neighbors[i];&lt;br /&gt;
                        let nKey = getPosKey(n.x, n.y, n.z);&lt;br /&gt;
                        &lt;br /&gt;
                        if (!visited.has(nKey)) {&lt;br /&gt;
                            if (Math.abs(n.x - px) &amp;lt;= radius &amp;amp;&amp;amp; Math.abs(n.y - py) &amp;lt;= radius &amp;amp;&amp;amp; Math.abs(n.z - pz) &amp;lt;= radius) {&lt;br /&gt;
                                let nId = World.getBlock(n.x, n.y, n.z).getId();&lt;br /&gt;
                                if (isWindow(nId)) {&lt;br /&gt;
                                    visited.add(nKey);&lt;br /&gt;
                                    stack.push(n);&lt;br /&gt;
                                }&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (emptyTargets.length &amp;gt; 0) {&lt;br /&gt;
                    let selectedBlock = existingBlock ? existingBlock : ((Math.random() &amp;lt;= lightRatio) ? blockLight : blockDark);&lt;br /&gt;
                    &lt;br /&gt;
                    groupsToBuild.push({&lt;br /&gt;
                        targets: emptyTargets,&lt;br /&gt;
                        selectedBlock: selectedBlock&lt;br /&gt;
                    });&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Chat.log(`§e[Урбанист] §fСформировано групп окон: §a${groupsToBuild.length}§f. Начинаю застройку!`);&lt;br /&gt;
&lt;br /&gt;
let changed = 0;&lt;br /&gt;
&lt;br /&gt;
// 2. Застройка через /setblock&lt;br /&gt;
for (let i = 0; i &amp;lt; groupsToBuild.length; i++) {&lt;br /&gt;
    let group = groupsToBuild[i];&lt;br /&gt;
&lt;br /&gt;
    for (let j = 0; j &amp;lt; group.targets.length; j++) {&lt;br /&gt;
        let target = group.targets[j];&lt;br /&gt;
&lt;br /&gt;
        Chat.say(`/setblock ${target.targetX} ${target.targetY} ${target.targetZ} ${group.selectedBlock}`);&lt;br /&gt;
        changed++;&lt;br /&gt;
        &lt;br /&gt;
        if (changed % blocksPerTick === 0) {&lt;br /&gt;
            Client.waitTick(1); &lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Chat.log(`§e[Урбанист] §fУспешно застроено блоков за стёклами: §a${changed}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Совет|Текст=Вы можете варьировать параметры в коде скрипта: например, устанавливать значение &amp;lt;code&amp;gt;const blocksPerTick&amp;lt;/code&amp;gt; в 10 и больше для быстрейшего расставления блоков света и темноты.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Преимущества метода ==&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Высокая скорость.&amp;#039;&amp;#039;&amp;#039; Обработка фасада крупного 16-этажного дома занимает считанные секунды.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Уникальность.&amp;#039;&amp;#039;&amp;#039; Генератор случайных чисел исключает повторяющийся паттерн «включённых» окон.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Автономность.&amp;#039;&amp;#039;&amp;#039; Метод работает через клиентский мод и не требует прав администратора сервера на установку плагинов.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Гибкость.&amp;#039;&amp;#039;&amp;#039; По аналогичной схеме можно заменять блоки, расставлять мелкий декор или автоматизировать маски для WorldEdit.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[MURS:Строителям]]&lt;br /&gt;
* [[Муроморск#Архитектура|Архитектура и застройка Муроморска]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:MURS:Строителям]]&lt;br /&gt;
[[Категория:Инструкции]]&lt;br /&gt;
[[Категория:Модификации]]&lt;/div&gt;</summary>
		<author><name>Tringoli</name></author>
	</entry>
</feed>