Клон Factorio на Godot 4.7. Мир, прототипы, рецепты, технологии, звуки и графика читаются из локальной установки Factorio 2.0.77 — в репозитории нет ни одного ассета игры, только код и извлечённые JSON-описания прототипов.
Проект — не «демка на один экран», а довольно полная симуляция завода: генерация бесконечного мира, конвейеры и вставщики, печи и сборочные машины, энергосети, жидкости, логистические роботы, поезда, схемы, загрязнение, битвы, исследования, ручное крафт-меню, сохранения и интерфейс в стиле оригинала.
![]() 1 |
![]() 2 |
![]() 3 |
![]() 4 |
![]() 5 |
-
Установите Godot 4.7 (проект собран под 4.7.2, Forward+).
-
Установите Factorio 2.0.77 (Steam/GOG/standalone).
-
Проверьте путь к игре в
project.godot:[openfactory] factorio_root="/home/cha14ka/.steam/steam/steamapps/common/Factorio"
Если игра лежит в другом месте — поменяйте значение.
-
Откройте папку проекта в Godot и нажмите F5 (Play). Главная сцена —
res://scenes/main.tscn.
При первом запуске читаются assets/data/manifest.json, ~3900 прототипов и
таблицы локализации; это занимает пару секунд.
В обычном запуске игра читает data/base и data/core из установленного
Factorio по абсолютному пути. Для переносимой сборки эти файлы нужно
упаковать внутрь .exe:
-
Установите Godot 4.7.2 с Windows export templates (Editor → Manage Export Templates).
-
Подготовьте ресурсы (нужен установленный Factorio 2.0):
python3 tools/prepare_export_data.py --force
Скрипт создаёт
factorio_data/data/{base,core,elevated-rails,quality}. Крупные PNG/OGG/шрифты переименовываются в*.bin, чтобы Godot не импортировал полтора гигабайта картинок как ресурсы; игра читает их из PCK черезFileAccess. -
Соберите Windows-исполняемый файл:
godot --headless --path . --export-release "Windows Desktop" build/OpenFactory.exe
Или одной командой:
./tools/build_windows.sh
В export_presets.cfg включён Embed PCK, поэтому рядом не нужен
отдельный .pck — получается один build/OpenFactory.exe.
./tools/build_linux.shРезультат — один исполняемый файл build/OpenFactory.x86_64 (PCK встроен).
Скрипт сам ставит ему executable-бит. Внутрь попадают PNG/OGG и три
используемых шрифта Lato; Lua-исходники Factorio, шейдеры, документация,
zip-банки звуков и неиспользуемые CJK-шрифты отфильтрованы. Сборка идёт из
чистой временной копии проекта, поэтому tmp_preview/, tools/, build/
и MCP-плагин в бинарник не попадают.
Проверочный размер Linux-сборки — 1.5 ГБ, это меньше Windows-сборки
(1.7 ГБ); внутри ровно один ELF-файл без внешнего .pck.
Важно:
- Размер готового файла будет порядка 1.5 ГБ: внутри лежат спрайты, звуки
и шрифты Factorio. DLC
space-ageне включается — проект использует base/core плюс небольшие elevated-rails/quality. - На машине, где запускается сборка, Factorio уже не нужен: игра сначала
ищет
res://factorio_data/packed_data.marker, затем использует упакованные данные. - Для запуска сборки на Windows нужен x86_64; экспорт с Linux тоже работает, если установлены шаблоны Windows.
- Не распространяйте такой
.exeбез разрешения Wube: ресурсы Factorio не входят в лицензию кода проекта.
| Действие | Клавиша / мышь |
|---|---|
| Движение | WASD / стрелки |
| Приблизить/отдалить | колесо мыши |
| Поставить предмет | ЛКМ по земле |
| Добыть вручную | удерживать ПКМ |
| Повернуть предмет | R (превью поворачивается вместе с ним) |
| Выбрать предмет под курсором | Q |
| Вернуть предмет с курсора / очистить | ПКМ или Q |
| Провод: взять/убрать | кнопки R/G справа от панели; Ctrl+W — цикл |
| Убрать провод | ПКМ, Esc или повторный клик по кнопке |
| Очистить ячейку панели | средняя кнопка мыши по ячейке |
| Ставить линию объектов | ЛКМ зажать и вести мышь |
| Поднять предметы рядом | F |
| Бросить предмет из рук под курсор | Z |
| Слоты быстрой панели | 1…9, 0; Shift+цифра — второй ряд |
| Инвентарь / крафт | E |
| Экипировка | G |
| Карта | M |
| Исследования | T |
| Производство | P |
| ALT-оверлей | Alt |
| Сохранить / загрузить | F5 / F9 |
| Пауза / меню | Esc |
| Вернуть камеру на инженера | Home |
- ЛКМ по слоту — выбрать его.
- Если на курсоре есть предмет (взят из инвентаря или машины), ЛКМ по слоту кладёт этот предмет в слот, ничего не расходуя.
- ПКМ по слоту — очистить слот.
- Средний клик по предмету в инвентаре — положить его в выбранный слот.
- Предмет, взятый на курсор, можно нести из инвентаря прямо в мир: после закрытия окна он остаётся на курсоре, рисуется поверх HUD и ставится ЛКМ.
src/
core/ Prototypes, Defs, Loc, FactorioAssets, FactorioStyle,
FactorioAudio, SpriteSource
world/ GameWorld (чанки 32×32, сущности), MapGenerator, Entity,
TileAtlas, DayCycle, RailGeometry, NoiseExpression/NoiseBasis
sim/ Simulation (60 Гц), BeltSim, FluidSystem, PowerNetwork, Energy,
LogisticNetwork, Trains, CircuitNetwork, Inventory,
InventoryLayout, Research, Objectives, Pollution, Alerts,
Statistics, Modifiers, EquipmentGrid, SaveGame, Blueprints
render/ WorldRenderer, EntityVisuals, SpriteCache, MapChunks
player/ Player (строительство, крафт, стрельба, быстрая панель),
Character (движение/майнинг/анимации), GameCamera
ui/ MainMenu, PauseMenu, InventoryWindow, EntityWindow,
ResearchWindow, ProductionWindow, EquipmentWindow, MapWindow,
Minimap, Toolbar, TooltipLayer, AlertPanel, ObjectivePanel,
VictoryScreen, ItemSlot, UiKit, FactorioStyle
game.gd bootstrap: мир → рендер → камера → игрок → HUD → меню
tools/ извлечение данных из Factorio и офлайн-превью (Python)
assets/data/ manifest, прототипы (246 типов, 3943 прототипа), локали en/ru
Ключевые идеи:
- Данные — из Factorio.
tools/factorio_extract/convert.pyпревращаетdata.rawв JSON; игра ничего не хардкодит про рецепты, спрайты и звуки. - Мир чанковый.
GameWorldгенерирует 32×32 чанка процедурно и кэширует их; руда лежит в отдельных байтовых массивах на клетку. - Симуляция фиксированная, 60 Гц. Рендер может быть любым, но завод всегда тикает с одной скоростью.
- Один проход отрисовки.
WorldRendererсобирает список прямоугольников (тайлы, руда, сущности, поезда, роботы, предметы на конвейерах) и рисует его одним_draw, а не тысячами нод. - Реальный интерфейс.
FactorioStyleсобираетThemeизgui-style.json: 9-slice рамки, шрифты Lato, цвета состояний.
Ниже — то, что было найдено и исправлено. Всё проверялось запуском игры из редактора Godot (без headless-прогонов).
SpriteSource заворачивал кадр спрайта в AtlasTexture с регионом и при этом
отдавал тот же регион как src_rect. draw_texture_rect_region применял
смещение региона дважды: спрайт сэмплировался из region + src_rect.
У инженера idle-лист начинается с y = 464, удвоенное смещение уходило за
пределы текстуры — персонаж был полностью прозрачным. У конвейеров так же
съезжали строки, поэтому вместо ленты рисовались чужие кадры.
Исправление: слой хранит полный лист и src_rect, без AtlasTexture.
Дополнительно у конвейера выставлен is_row = true (двадцать строк листа —
это формы ленты, а не повороты), а таблица прямых участков исправлена на
row 0 = восток, 1 = запад, 2 = север, 3 = юг.
В текущей сборке пустое меню не воспроизвелось (кнопки строились и были
кликабельны), но причина была устранена на уровне раскладки: UiKit.fill_parent
раньше задавал только size и полагался на событие resize, которого при старте
может не быть — тогда CenterContainer оставался нулевого размера и колонка
уезжала за экран. Теперь fill_parent ставит полноэкранные якоря (а в headless-режиме ещё и
явный размер), и MainMenu.open() принудительно перекладывает контейнеры. HUD (быстрая панель,
тулбар, цели) на титульном экране скрыт, чтобы не перекрывать меню.
После починки спрайтов превью постройки стало видимым и действительно
поворачивается. Превью теперь строится из предмета на курсоре, если он там
есть, иначе из выбранного слота панели, и использует актуальный
build_direction.
Быстрая панель стала настоящей панелью Factorio: 2 ряда × 10 слотов. Слоты кликабельны: ЛКМ с предметом на курсоре назначает предмет, ПКМ очищает, средний клик по предмету в инвентаре кладёт его в выбранный слот. Старые сохранения с 8 слотами при загрузке дополняются до 20.
Раньше закрытие инвентаря возвращало предмет обратно, а строительство смотрело только на быструю панель. Теперь:
- предмет на курсоре остаётся на курсоре после закрытия окна;
- поверх HUD рисуется иконка предмета и количество;
build_at_cursor()/ призраки / поезда / превью используютheld_item()(курсор → панель);- при постройке расходуется именно тот предмет, из которого строили.
Следствие бага №1: конвейер рисовался кадром из неправильного места листа, а
для многих направлений — вообще за пределами атласа. После исправления
AtlasTexture + is_row лента рисуется корректно для всех направлений и
поворотов.
Замеры до исправлений (окно 1600×900, пустой мир):
| Что | Было |
|---|---|
| Сборка списка отрисовки | ~16.4 мс каждый кадр |
| Перестройка миникарты (34 000 клеток) | ~107 мс каждые 20 кадров |
| Генерация одного чанка | ~145 мс |
| FPS | ~30–50 с рывками |
Что сделано:
- Кэш земли в рендерере. Тайлы и руда кэшируются по клетке и сбрасываются только при изменении мира/шага анимации воды. Сборка списка — ~1.2 мс.
- Тег для спрайтов руды.
SpriteCacheбольше не строит структурный «отпечаток» всего дереваstagesдля каждой клетки руды. - Грубая выборка шумовых выражений. Температура и
auxсчитаются на сетке с шагом 16 клеток и билинейно интерполируются вместо запуска VM Factorio на каждую клетку. Генерация чанка — ~10 мс. - Миникарта на чанках.
MapChunksдержит 32×32 текстуры чанков; миникарта рисует их, а не пересчитывает 34 000 клеток. Перестройка — практически бесплатная, обновление каждый кадр. - Карта на тех же чанках. Полноэкранная карта собирается из кэша и перестраивается только при изменении мира/зума/панорамы (~5.5 мс), причём не генерирует неисследованные чанки (они остаются тёмными).
- Общий кэш чанков для миникарты и карты.
- HUD и позиционный звук — 10 Гц, а не 60: они обходят весь список сущностей.
- Корректный слой ночи (
CanvasLayer1, под интерфейсом) и блокировка ввода под меню/паузой.
После: 60 FPS в окне 1600×900, без периодических 100-мс фризов. Чанк всё ещё генерируется ~10 мс — это разовый рывок при входе в новую территорию, а не постоянные подвисания.
- На миникарте появилась синяя точка инженера с тёмной обводкой.
- На полной карте — синий маркер-оверлей, который остаётся видимым при панорамировании и не заставляет перестраивать всё изображение при каждом шаге.
- Карта при открытии центрируется на инженере (раньше она открывалась
смещённой:
centre_on()читал размерTextureRectдо первой раскладки и получал минимальные 640×480 вместо реального размера окна).
_spawn_cell выбирался в _build_starter_factory() до того, как ставились
солнечные панели и столбы, и проверял только одну клетку. Панели 3×3 накрывали
эту клетку позже, а Character.blocked() считал занятыми все четыре угла — игрок
не мог шагнуть ни в одну сторону.
Теперь клетка спавна выбирается в конце постройки: _find_spawn_cell() ищет
площадку 3×3 без сущностей и воды, а _ensure_character_free() дополнительно
выталкивает инженера из занятой клетки при старте, загрузке сохранения,
возрождении и новой игре. На всякий случай Character.tick() разрешает
движение, если персонаж уже стоит внутри объекта, чтобы из такого состояния
можно было выйти вручную.
Две причины:
- окно инвентаря было полноэкранным
MOUSE_FILTER_STOPи съедало клики по миру; теперь прозрачная подложка пропускает мышь, аPlayerпри открытом инвентаре принимает клики по миру (клавиатура и движение по-прежнему блокируются, чтобы поиск в крафте не двигал персонажа). Проверено реальнымViewport.push_input(), а не только вызовом метода; - при неудачной установке (занятый футпринт, дерево, вода) теперь играет
cannot_build, а превью заранее краснеет.
EntityVisuals для типа inserter возвращал только platform_picture и
выходил из цикла — hand_base_picture не рисовался никогда. Теперь:
- платформа выбирается из четырёх кадров листа по направлению (раньше всегда брался северный кадр);
- рука рисуется как линия от центра к кисти и настоящий спрайт
hand_open/hand_closed, который поворачивается по дуге от клетки забора к клетке выгрузки поstate["swing"]; - в кисти рисуется иконка предмета, если манипулятор что-то несёт;
- призрачные манипуляторы тонируются синим.
При наведении на манипулятор поверх мира рисуются направляющие: синяя линия и рамка клетки забора, оранжевая стрелка с наконечником и рамка клетки выгрузки.
_can_insert() проверял только наличие места. Добавлена _accepts_input():
фургон/сундук/машина принимают что угодно, а печь — только топливо или
ингредиент плавки, сборочная машина — только ингредиент своего рецепта (или
любого рецепта своих категорий, пока рецепт не выбран), лаборатория — только
научные пакеты, турель — только подходящие боеприпасы, кузня/реактор — топливо,
маяк — модули. Ингредиенты категорий кэшируются, поэтому проверка не стоит
ничего на каждом тике.
- Кадр анимации ленты теперь считается из
simulation.tick,speedпрототипа иframe_countлиста; жёлтая, красная и синяя ленты крутятся с разной скоростью. Это работает и для splitter/underground-belt. - Предметы на ленте сортировались по своей lane-координате и на ближней полосе оказывались под спрайтом ленты — отсюда «что-то мелькает». Теперь предмет сортируется чуть выше центра своей клетки и всегда рисуется поверх ленты; учитываются и splitter/underground, а не только transport-belt.
- В
_drawдобавлена отрисовка линейных записей — заодно стали видны провода схем, которые раньше попадали в draw-list, но пропускались как «без текстуры».
hud (дебаг-текст и панель целей) был на CanvasLayer 0, поэтому ночной слой
(1) затемнял его. Теперь HUD — слой 2, быстрая панель — 2, окна — 3, а ночь —
1: ночь ложится только на мир и инженера.
Добавлен шейдер src/render/night_flashlight.gdshader: из ночной заливки
вырезается мягкий круг вокруг инженера, радиус масштабируется с зумом. Это и
есть фонарик — мир вокруг игрока виден, интерфейс не затрагивается.
Добавлена GameWorld.can_place_terrain() и она используется в постановке
игроком, в превью и в чертежах:
- электрический и простой (burner) бур ставится только если под его
футпринтом есть подходящая руда (
resource_categories, напримерbasic-solid). На пустой траве/песке — отказ и звукcannot_build; - всё остальное можно ставить без ограничений по типу земли — на траве, на руде, в пустоте;
- на воде нельзя ставить ничего (ни машины, ни трубы), проверяются
water,water-shallow,deepwaterи их варианты; - засыпка (landfill) — исключение: это предмет с
place_as_tile, он заменяет воду наlandfill, если текущий тайл входит в егоtile_condition. Для неё добавлена отдельная постановка тайла (_build_tile) и отрисовкаmaterial_backgroundвTileAtlas(иначе засыпка была бы валидной, но невидимой).
Призраки (ghosts) и чертежи без материалов по-прежнему разрешены где угодно —
это планы, а не постройки. Blueprints.place() при нехватке грунта оставляет
призрак, а не строит машину.
Настоящая причина «объекты не ставятся, надо тыкать много раз»: W была
одновременно движением вперёд и переключением провода
(красный → зелёный → выключен). Обычный шаг вверх случайно вооружал
инструмент провода, после чего ЛКМ рисовала провод вместо установки объекта.
Ещё одно W переключало цвет, третье — выключало, и строительство снова
работало «на короткое время» — ровно тот симптом, который был описан.
Теперь:
W— только движение;- провод берётся кнопками с иконками красного/зелёного провода справа от панели вещей (как shortcut bar в Factorio); клик по кнопке берёт провод, повторный — убирает;
- активная кнопка подсвечивается зелёным, в HUD пишется
wire: red/green ...; Ctrl+Wостался быстрым циклом: красный → зелёный → выключен;ПКМиEscубирают провод;- без активного провода клик по печи открывает её окно, а не рисует провод.
Проверено: после W wire_colour пуст и печь ставится; клик по печи при
выключенном проводе открывает инвентарь печи; при активном проводе — тянет
провод; после отмены провод снова открывается инвентарь.
- Конвейеры, электрические столбы и манипуляторы больше не считаются стенами:
Character._blocks()пропускаетtransport-belt,underground-belt,splitter,inserterи все вариантыelectric-pole. - Если инженер стоит на конвейере, лента сама везёт его в своём направлении
со скоростью
speedпрототипа: 0.03125 tile/tick у жёлтой, 0.0625 у красной, 0.09375 у синей. Это та же скорость, с которой движутся предметы на ленте. В паузе/меню лента не двигает игрока. - При наведении на столб рисуются: зелёный квадрат supply area и оранжевый
круг
maximum_wire_distance— дальность, на которой столб соединяется медным проводом с другим столбом. PowerNetworkтеперь хранитlinks— пары соединённых столбов. Рендерер рисует по ним медные провода от реальныхconnection_pointsна верхушках столбов. Логика сети и картинка используют один и тот же список, поэтому провод не может появиться без электрического соединения и наоборот.
Rнад уже поставленным поворачиваемым объектом поворачивает сам объект, а не толькоbuild_direction; если под курсором такого объекта нет — как раньше, крутится предмет для постановки. Это работает для буров, лент, манипуляторов и других машин.- При наведении на бур рисуется жёлтая стрелка в клетку выдачи — сторона видна до включения питания.
- Выдача стала настоящей:
_update_miner()считает мировую клетку поvector_to_place_result, повёрнутому наdirection. Если там стоит конвейер (transport/underground/splitter), руда кладётся прямо на него; если клетка пустая — на землю кладётсяitem-on-ground(его можно поднять кликом,Qили вставщиком); только если клетка занята/вода — в выходной инвентарь бура. - Электрические буры стартового завода теперь ставятся с
direction = 8(юг): выход смотрит на вставщик и ленту, а не в обратную сторону.
Проверено: у стартовых буров direction = 8, R меняет его на 12 и выходная
клетка уезжает на запад; burner-бур с выходом на ленту кладёт предмет в
BeltSim (1 предмет на ленте), а его внутренний output остаётся пустым.
Скриншот со стрелкой — tmp_preview/drill_output.png.
- Выходная клетка считается из
vector_to_place_resultи поворота бура. - Если впереди лента — предмет сразу на ленте; если земля — создаётся
item-on-groundсо своей иконкой и количеством; если клетка занята или вода — используется выходной инвентарь. - Наземные предметы можно поднять кликом,
QилиПКМ; вставщики тоже умеют брать их изitem-entity.
Qнад объектом/предметом на земле берёт предмет в руки (переносит из инвентаря или с земли), а не просто выбирает слот.- Если в руках уже что-то есть,
Qочищает руку в любом месте, даже если курсор вне мира; при пустой рукеQпо пустой земле ничего не делает. - Если такого предмета у игрока нет,
Qвсё равно даёт его в руки с количеством 0; следующий клик ставит призрак. - То же для нижней панели: если выбранный предмет отсутствует в инвентаре, клик по земле ставит призрак (превью при этом синее).
- Клик по пустой ячейке панели открывает окно выбора всех предметов, открытых текущими исследованиями (плюс всё, что уже есть в инвентаре), с поиском и иконками. Выбранный предмет записывается в эту ячейку.
Над электрическим устройством без питания (вставщик, электрический бур,
сборочная машина и т.п.) рисуется красный значок electricity-icon-red.png
из Factorio. Проверка идёт через PowerNetwork.satisfaction_for(), поэтому
значок пропадает, как только появляется питание.
- У электрического бура базовая картинка статична, а движение головы и
выдача руды лежат в
graphics_set.working_visualisations. EntityVisualsтеперь добавляет к базеalways_draw-визуализации, у которых есть кадры;WorldRendererпередаёт им реальный animation frame (0.4 кадра/тик, 60-кадровый цикл), а не жёсткий 0.- Заодно оживают анимации burner-бура, печей и сборочных машин: их листы
тоже используют
frame_count. - Проверено: у головы бура и выходного лотка меняется
src_rectмежду кадрами; два снимка через 8 кадров отличаются; FPS 60.
Prototypes.item_sort_key()собирает ключgroup order / subgroup order / item order / nameизitem-group,item-subgroupи самого предмета.- Инвентарь персонажа сортируется этим ключом при каждом обновлении окна: логистика, производство, промежуточные продукты, сырьё и т.д. идут группами, а не в порядке подбора.
- Тот же ключ используют список крафта (
craftable_recipes()) и окно выбора предмета для панели (ItemPicker), поэтому все три списка сортируются одинаково. - Сортируется только инвентарь персонажа; сундуки и слоты машин сохраняют свой порядок.
Проверено: ключи инвентаря, крафта и выбора панели монотонно возрастают;
скриншот tmp_preview/inventory_sorted.png.
MapGenerator._spawn_decoratives()заполняет каждый новый чанк деревьями (tree-01…tree-09, сухие варианты для песка/пустыни) и камнями (big-rock,huge-rock,big-sand-rock).- Лесные пятна задаёт отдельный шум
_forest: в лесу деревья идут плотно, вне него — редкие одиночные. Камни распределены по суше. - Каждая клетка будущего дерева/камня проверяется: не вода, не руда, не занята другим объектом. Объекты, выходящие за границу чанка, не ставятся, чтобы генерация не рекурсировала.
GameWorld.place_entity()теперь вообще не принимает физический объект ни на одной водной клетке (ghost — исключение, это план). Стартовая фабрика ищет площадку только на суше, а деревья/камни в её пятне вычищаются перед постройкой.- Деревья при добыче дают дерево, камни — камень/уголь, по
minableиз прототипа.
Проверено: в новом мире обнаружено больше 1500 деревьев и около 90 камней,
ни одного на воде или глубокой воде; стартовая фабрика снова ставит все
33 объекта; генерация чанка — 8–24 мс (лесной чанк дольше), вне леса FPS
снова ~60, в очень плотном лесу около 48 из-за количества крупных спрайтов.
Скриншот tmp_preview/forest.png.
Fсобирает в инвентарь наземные стопкиitem-entityи предметы с лент в радиусе 4.5 тайла вокруг инженера. Если в инвентаре нет места, остаток остаётся на земле/ленте.Zбросает предмет из рук в клетку под курсором: на землю создаётсяitem-on-ground, на ленту предмет кладётся черезBeltSim.try_insert. На воду и занятые чужие клетки бросок запрещён.- При разрушении ленты (в том числе ручной добычей) всё, что ехало на ней,
переходит в инвентарь через сигнал
mined; сама лента тоже попадает туда.
Проверено: Z бросил 10 лент на землю, F поднял их (+10 в инвентаре);
Z на ленту положил 5 угля, F снял их с ленты; после поломки ленты 5 угля
перешли в инвентарь, лента исчезла. FPS 60.
Inventory.insert()возвращает остаток, а не количество, которое поместилось. ВInventoryWindow._transfer()иEntityWindowэтот остаток вычитался из размера стака как «вставленное» число. Из-за этого курсор оставался полным, хотя предмет уже лежал в инвентаре — каждый следующий клик добавлял ещё столько же.- Теперь считается
inserted = amount - leftover, и курсор уменьшается ровно на то, что реально попало в слот. - Заодно исправлен правый клик в окне машины: он уменьшает руку только тогда, когда предмет действительно вставился.
Проверено: 20 меди в руках → клик по пустой ячейке: в инвентаре 20, в руках пусто; повторные клики по пустым ячейкам оставляют 20, а не 40/60/80.
- У Factorio листы
tree-*-normal.png— это normal map, а не крона. Раньше он рисовался вместе с деревом, а общий animation frame перебирал три кадраleaves/normal(0..2), поэтому дерево бесконечно мигало между полным и «лысым». - Теперь рисуются
trunk+leaves+shadow; normal map не используется. - Кадр листвы выбирается как стадия: 0 — полная, 1 — поредевшая, 2 — почти голая. Стадия зависит от загрязнения чанка (50/300 единиц) и здоровья дерева, а не от времени, поэтому спрайт не переключается каждый кадр.
- Сухие/мёртвые деревья берут один вариант из
pictures, а не все сразу.
Проверено: src_rect листвы стабилен между тиками; при загрязнении
0 / 60 / 400 стадии 0 / 1 / 2. Скриншот tmp_preview/tree_fixed.png.
- Причина просадки: при
zoom 0.5в кадр попадало около 8 400 текстурированных прямоугольников — почти всё это были отдельные клетки земли и сотни деревьев. - Теперь при
_render_scale <= 0.70земля рисуется не по клеткам, а готовыми цветными текстурами чанковMapChunks(32×32). Несгенерированные чанки остаются тёмными и не генерируются в кадре. - Деревья, камни и лежащие предметы не рисуются при
_render_scale < 0.65; мелкие вставщики/столбы/трубы — при< 0.22; предметы на лентах — при< 0.25. Машины, ленты и сам инженер остаются. - Всё, что вне камеры, по-прежнему отсекается по
visible_world_rect(), а симуляция продолжает тикать независимо от рендера.
Проверено: zoom 0.5 — 8 405 → ~80 предметов отрисовки, FPS 21 → 60;
zoom 0.25 — ~114 предметов, 56–60 FPS; zoom 0.1 — ~97 предметов, 60 FPS,
тик за 30 кадров вырос на 29. Скриншот tmp_preview/zoom_lod.png.
- Factorio хранит огонь печи, нагреватели электрической печи, дым бура и
пропеллеры в
graphics_set.working_visualisations, а не в базовойanimation. EntityVisualsтеперь собирает этот список для любого прототипа:always_draw-части идут в обычные узлы, остальные — вworking_nodes. Рендерер показываетworking_nodesтолько когдаentity.state.progress > 0, то есть машина действительно плавит/крафтит.- Текстуры с
draw_as_lightиblend_mode: additive— это маски света. Один batched-canvas не может включить additive-блендинг на отдельный прямоугольник, поэтому такие слои теперь пропускаются: раньше полностью непрозрачная light-текстура рисовалась поверх печи и превращала её в чёрный прямоугольник. - Базовая текстура печи и прозрачный огонь остаются видимыми.
Проверено: у плавящейся каменной печи рисуются stone-furnace.png и
stone-furnace-fire.png, а stone-furnace-light.png/ground-light.png
больше не перекрывают её. В простое огня нет. Скриншот
tmp_preview/furnace_smelting2_crop.png.
«Single player» в главном меню теперь открывает окно настроек, а не сразу создаёт карту. После «Start game» параметры применяются к генератору и сохраняются вместе с игрой.
- Map: вода (
water) и влажность/сухость (moisture). Чем суше, тем меньше деревьев; параметр одновременно задаётgenerator.tree_density. - Ore: для каждой руды (железо, медь, уголь, камень, уран, нефть) — frequency / size / richness. Частота уменьшает расстояние между патчами, размер меняет радиус, богатство — запас руды и её стадии.
- Creative mode — галочка «everything unlocked»: при старте вызывается
Research.unlock_all(), все 196 технологий и 211 рецептов уже открыты. - Enemies: скорость эволюции (
Simulation.evolution_speed) и начальная дистанция ульев от стартовой зоны в чанках. Безопасная зона теперь считается вокруг фактического спавна игрока, а не вокруг (0,0); ульи, попавшие внутрь неё до выбора точки спавна, удаляются. - Настройки генератора, скорость эволюции и безопасная зона входят в save и восстанавливаются при загрузке.
Проверено: 22 слайдера (2 карты + 18 руды + 2 врагов); установка
water=0.4, moisture=0.5, copper frequency/size/richness=2/1.5/3,
evolution=3, nest distance=2 применяется к world.generator и sim;
окно закрывается, HUD снова появляется, FPS 60. Скриншот
tmp_preview/world_settings2.png.
- Раньше
Player._rotate_drill_under_cursor()трогал толькоmining-drill. Теперь общийGameWorld.rotate_entity()вызывается для спискаROTATABLE_TYPES: ленты, подземные ленты, сплиттеры, вставщики, буры, печи, сборочные машины, котлы, генераторы, насосы, трубы-в-землю, турели, ворота и т.п. - Поворот идёт на 90°, а footprint пересчитывается: сплиттер 2×1 становится
1×2, origin сдвигается вокруг центра, все клетки
_entity_ownerпереприсваиваются, словарьentitiesобновляется. - Если новая площадка пересекает другой объект или воду, поворот
отменяется и играет
cannot_build. - После поворота помечаются dirty power/fluids/logistics, поэтому провода, трубы и логистика пересобираются; симуляция продолжает работать.
Проверено: лента 4→8, вставщик 0→4, бур 8→12, сплиттер 2×1→1×2 с корректными owner-клетками; FPS 60, ошибок в логе нет.
- Сохранения лежат в
user://saves/<имя>.json; имя задаётся в диалоге «Save game». Староеuser://openfactory-save.jsonпоказывается какAutosave (legacy)и тоже доступно для загрузки. - В главном меню появилась кнопка Load game, а в паузе Save game и
Load game открывают общий диалог: список сохранений с именем, seed,
тиком, датой и пометкой
[creative], выбор мышью и кнопка Load. Continueзагружает самое новое сохранение;F5пишетQuicksave,F9загружает самое новое.- Флаг creative записывается в save и восстанавливается при загрузке.
Проверено: сохранения Creative Test A, Creative Test B и Pause Save
видны отдельными строками; загрузка выбранного Creative Test A вернула
игру с creative-флагом и 196/196 технологиями; FPS 60. Скриншот
tmp_preview/save_load2.png.
- Burner mining drill / R: визуальные узлы, выбравшие направление внутри
EntityVisuals, возвращалиdirection = 0, поэтомуSpriteCacheкэшировал первый (северный) кадр под одним тегом. Тег теперь включаетentity.direction; текстура burner/electric drill поворачивается вместе с R. - Q и дюп:
pick_from_cursor()теперь действительно извлекает стак из инвентаря (inventory.extract), а не копирует его количество в руку. Повторный Q возвращает ровно тот же стак. - Q по пустому месту: если в руке выбран предмет нижней панели (не cursor), Q выбирает пустой слот панели и очищает руку; если пустых нет — очищает текущий слот.
- Госты: правый клик по ghost удаляет его сразу, без времени добычи;
mine_at_cursor()делает то же. - Выдача буров:
_update_miner()теперь кладёт руду не только на ленту, но и в подходящий контейнер/машину в клетке выдачи — сундук, вход печи, ассемблера, лаборатории. Если цель не принимает предмет или полна, используется прежний выход/земля. - Окна: при открытии ящика/печи/машины рядом автоматически открывается
инвентарь персонажа. Окна раскладываются слева/справа, курсор рисует
только одно из них; перетаскивание работает между обоими. Полноэкранный
корень окна машины теперь
MOUSE_FILTER_IGNORE— иначе он перехватывал клики по соседнему инвентарю и из него нельзя было ничего взять.
Проверено: burner drill N→E после R; Q с 20 лентами: 0 в инвентаре и 20 в
руке, второй Q возвращает 20; ghost исчезает мгновенно; бур положил железо
прямо во вход печи; инвентарь и железный сундук открыты рядом
(tmp_preview/entity_inventory.png). FPS 60, ошибок нет.
- Средняя кнопка мыши по ячейке нижней панели очищает эту ячейку; предмет остаётся в инвентаре и может быть назначен снова.
- Причина «из инвентаря ничего не берётся»:
EntityWindowзанимал весь экран сMOUSE_FILTER_STOP. Теперь корень окна игнорирует мышь, а клики ловит только его рамка; соседний инвентарь снова кликается. - Проверено: средняя кнопка очистила слот 3; при открытом железном сундуке клик по слоту инвентаря поднял wooden-chest ×4 в курсор; FPS 61.
- Пауза → Save/Load: пауза теперь скрывается на время диалога, а сам
диалог поднимается
move_to_front(). Раньше полноэкранное меню паузы оставалось поверх и перехватывало мышь, поэтому кнопки не нажимались. После загрузки из паузы симуляция снова снимается с паузы (sim.paused = false), а не остаётся замороженной. - Креатив: клик по рецепту в крафте выдаёт его продукт прямо в курсор,
без ингредиентов и очереди. Пустая ячейка панели в креативе показывает все
item-like прототипы (249 предметов), а не только рецепты; постройка в
креативе не тратит предметы (
held_count/consume_held). - Инвентарь/крафт: колонки поменяны местами — инвентарь слева, крафт
справа. В крафте появились вкладки категорий (
All,Combat,Intermediate products,Logistics,Production,Space), а инвентарь по-прежнему сортируется поitem_sort_key. - Панель оружия: внизу слева появилась сетка 4×2: кнопка инженера,
три слота оружия, кнопка сетки брони и три слота патронов. Выбранное
оружие подсвечено, патроны показывают количество; клик по оружию выбирает
его, кнопка игрока/брони открывает
EquipmentWindow, если у брони есть сетка. В стартовый набор добавленыpistolи 10firearm-magazine.
Проверено: пауза → Save → Cancel реальным кликом мыши; creative 196/196
технологий и 211 рецептов, клик по рецепту дал предмет; инвентарь слева/крафт
справа с 5–6 вкладками; панель оружия показывает pistol и 10 патронов;
FPS 60. Скриншоты inventory_swapped3.png,
weapon_panel4_crop.png.
- Камни: вариант выбирается по позиции, но тег спрайт-кэша не содержал
позицию, поэтому все камни могли показывать первый вариант. Тег теперь
включает
origin, аsimple-entityиспользует кадр 0; разные варианты камней стабильны и не «анимируются». - Offshore pump: добавлена отрисовка четырёх направлений из
graphics_set.animation; насос теперь 1×1 (а не 2×2), ставится только на воду у берега, а его выход направлен на сушу. Собственная клетка воды считается источником, так что насос качает воду даже на одиночной прибрежной клетке; вода уходит в трубу/бойлер. - Машина:
Fрядом с машиной сажает инженера,Fвнутри — высаживает. WASD двигает машину как настоящую сущность (проверка клеток, вода, столкновения), камера следует за ней; на экране машина заменяет инженера. - Железная дорога: рельсы стали проходимыми для игрока; поезд ставится
на рельс (направление берётся из самой рельсы). При укладке прямые
соединяются автоматически, а
Rзадаёт направление выхода — если оно совпадает с выходом изгиба, ставитсяcurved-rail-a/b. - Space Age DLC: item-like прототипы с иконками
__space-age__/__quality__и группыspaceотфильтрованы; вместе с ними убраны их рецепты, технологии и ресурсы. Осталось 172 item-прототипа, 249 item-like, 211 рецептов, 196 технологий и 6 ресурсов; базовые вещи вроде поезда, машины и рельсов сохранены. - Биттеры/FPS: гнёзда больше не невидимы (
graphics_set.animations), юниты рисуются; поиск цели у кусак выполняется раз в 20 тиков, число принадлежащих гнезду юнитов считается один раз за тик, а общий лимит активных кусак — 200. Это убирает тяжёлые O(units × entities) проходы.
Проверено: насос дал 3.5 воды в трубу; машина проехала по WASD и F
входит/выходит; изгиб рельса выбран после R; поезд поставлен; гнездо
рисуется (spawner-idle.png); 100 кусак — ~50 FPS; DLC-предметы отсутствуют.
У oil refinery и chemical plant graphics_set.animation оказалась не одной
слоистой анимацией, а таблицей из четырёх направлений (north/east/south/ west). Старый код для assembling-machine передавал эту таблицу прямо в
SpriteCache, резолвер не находил слоёв и машина рисовалась пустой.
Исправление сделано общим: EntityVisuals._direction_animation() выбирает
направление, разворачивает вложенный animation и массив, после чего
assembling-machine, furnace, mining-drill, offshore-pump и общий
fallback используют один и тот же путь. Это закрывает не только эти две
машины, но и любые другие прототипы с такой же формой графики.
Проверено: в draw-list появились
chemical-plant.png, oil-refinery.png и их тени; assembling-machine-3
по-прежнему рисуется. README-раздел 37.
- Турбины/трубы: соединения fluid box заданы в локальной системе
сущности, но
FluidSystem.connection_targets()не поворачивал их вместе сentity.direction. У повёрнутой турбины вход смотрел на север/юг, хотя трубы были с востока/запада. Теперь направление порта поворачивается наentity.directionдля всех машин, а не только для offshore pump. Проверено на сохраненииelectro test: все четыре турбины попали в сеть из 20 элементов, получили пар (139 единиц, 165°C) от бойлеров. - Конвейеры: ЛКМ по
transport-belt,underground-beltилиsplitterбольше не открывает окно машины; у них нет меню. Добыча ПКМ сохранена. - Постановка линией: зажми ЛКМ с предметом в руке и веди мышь — объекты ставятся по всем клеткам линии, включая пропущенные при быстром движении. Для конвейеров/сплиттеров/подземных конвейеров направление автоматически берётся из направления движения. Постановка останавливается, когда предметы кончились; в креативе работает бесконечно. Ctrl, Shift, чертежи, поезда и рельсы сохраняют свои отдельные режимы.
- Burner inserter теперь при пустом топливном слоте и нулевом заряде сначала берёт топливо из своей pickup-цели себе в fuel-инвентарь, и только потом переносит предметы дальше. Это работает и для старых сохранений, где все три инсертера стояли с пустым топливом.
- При постановке burner-сущность получает
initial_fuelиз прототипа: burner inserter — 25% wood, чего хватает на несколько первых поворотов. - Если топливо уже есть в fuel-слоте, inserter не объедает ленту постоянно: он переносит уголь дальше, пока запас не кончится.
Проверено на electro test: после подачи угля на ленты все три burner
inserter’а заправились сами, бойлеры 2 и 3 получили уголь, генерация осталась
3.84 МВт при потреблении 0.72 МВт. Без ручной заправки инсертеров цикл
запускается.
- Симуляция теперь ставит
working_tickтолько когда машина действительно работает: буровой цикл, crafting-прогресс, лаборатория, производство пара бойлером, выработка турбины, перекачка offshore pump. - Рендерер для фабричных типов (
furnace,assembling-machine,mining-drill,boiler,generator,rocket-silo,lab,offshore-pump) замораживает базовый кадр на0, еслиworking_tickне совпадает с текущим тиком. Рабочиеworking_visualisationsтоже не рисуются у остановленной машины. - Турбина анимируется только при
available_power > 0; бойлер — только когда реально производит пар, а не просто имеет заряд топлива. - Это работает и для oil refinery/chemical plant, furnace, assembler, drill, lab и rocket silo, а не только для двух машин из жалобы.
Проверено на electro test: бойлер 1 и все четыре турбины имеют активный
кадр, бойлеры 2 и 3 без топлива заморожены на первом кадре.
- Форма трубы выбирается по соседям (
pipe_picture_key), но тег спрайт-кэша не включал выбранную форму. Если несколько offshore pump подключались к одному трубопроводу, труба могла сохранить старую картинку из кэша и при следующем перестроении выглядеть так, будто соединение переключилось само. - Теперь выбранная форма входит в
SpriteCache-тег. Проверено с четырьмя насосами в одной fluid-сети (network size 15): ключи труб остаются стабильными между тиками.
- Направление кадров конвейера уже совпадает с нарисованными стрелками;
попытка проигрывать их в обратную сторону дала движение «задом наперёд».
_belt_animation_frame()снова считает фазу вперёд. - У поворотов
E->NиN->Eбыли перепутаны местами ряды 4 и 5; из-за этого стрелка на одном из зеркальных углов смотрела в обратную сторону. Теперьd0 right (E->N) = 4,d4 left (N->E) = 5; для east/south остаютсяd4 right = 8,d8 left = 9. Кадры идут вперёд, как на прямых. - Предметы на изогнутом конвейере больше не едут по прямой линии выходного
направления.
BeltSim.item_world_position()строит четверть окружности от входной стороны к выходной и смещает полосы по нормали к траектории, так что предметы лежат на полотне, а не летят над поворотом. curve_input_side()использует то же определение питающей стороны, что и выбор спрайта поворота, поэтому картинка и траектория предметов совпадают.
inserter-stack-size-bonusиbulk-inserter-capacity-bonus— разные бонусы. Раньше оба прибавлялись к руке любого манипулятора, поэтому при полной исследованности получалось1 + 11 + 2 = 14. Бонус stack size относится к bulk/stack-манипуляторам; обычный манипулятор теперь имеет максимум1 + 11 = 12предметов.- Выгрузка на конвейер больше не кладёт всю руку одним belt-item с
count = 12/14. Манипулятор сбрасывает по одному предмету за тик, а оставшееся держит в руке; в сундук по-прежнему переносится вся рука (это штатное поведение Factorio). - Энергия за поворот списывается один раз, когда рука опустела, а не за каждый предмет.
Проверено при 196 исследованиях: ёмкость руки 12, на конвейере все
предметы имеют count = 1, в контейнер уходит вся рука за один перенос.
- Иконка ALT-режима больше не висит над машиной. Она рисуется по центру
объекта (
entity.center()), поэтому одинаково работает для одно- и многоклеточных машин. - Под иконкой рисуется мягкое радиальное затемнение: оно гасит шумный фон спрайта вокруг значка, но полностью исчезает к краю и не закрывает саму машину.
- ALT-значки перенесены в слой
badges, который рисуется после всех мировых объектов. Раньше они попадали вbodiesи у многоклеточных машин могли оказаться под собственной графикой.
Проверено на сборочной машине с рецептом iron gear wheel и на буровом машине: значок стоит в центре, вокруг видно затемнение, сам объект остаётся читаемым.
FactorioAudioтеперь держит два музыкальных проигрывателя на отдельном busMusicи кроссфейдит треки за 2.5 секунды.- Меню играет
main-menu.ogg. В игре плейлист зависит от эпохи исследований: ранняя (after-the-crash,first-light,automation), средняя (the-search-for-iron,gathering-horizon,research-and-minerals) и поздняя (the-oil-industry,expansion,beyond-factory-outskirts,turbine-dynamics). - Когда трек заканчивается, автоматически выбирается следующий из текущего плейлиста; один и тот же трек не повторяется, если есть альтернатива.
- Смена эпохи или переход между меню и игрой сразу запускает кроссфейд.
Проверено в редакторе: меню играет main-menu.ogg, после старта включился
first-light.ogg, при полной исследованности — beyond-factory-outskirts.ogg,
а по окончании трека автоматически начался the-oil-industry.ogg.
- Рука, кисть и предмет в ней раньше попадали в
bodiesи сортировались по глубине вместе с миром. Конвейер или труба перед манипулятором могла перекрыть кисть. - Теперь рука и кисть рисуются в верхнем слое
badges, как ALT-значки: конвейер, труба, стена или машина перед манипулятором больше не могут закрыть руку. - Основание манипулятора по-прежнему сортируется по глубине, поэтому сам манипулятор остаётся на своём месте; поверх всего идёт только рука.
Проверено на манипуляторе у конвейера: рука и кисть не проваливаются под ленту.
Player._plan_recipe()строит дерево рецептов. Если ингредиента нет, но его можно сделать вручную, сначала планируется дочерний крафт. Например, транспортная лента использует железную шестерёнку, а шестерёнка — две железные пластины.- Задания кладутся в очередь в порядке зависимостей: сначала промежуточные компоненты, затем родительский предмет. Ингредиенты списываются при старте каждого задания, поэтому шестерёнка успевает появиться в инвентаре.
craftable_count()считает максимум с учётом всех вложенных рецептов и побочных выходов. Shift ставит в очередь 5 крафтов, Ctrl+Shift — это максимум, правый клик — тоже максимум.- Старые сохранения с очередью без флага
startedзагружаются как уже оплаченные, чтобы ингредиенты не списались второй раз.
Проверено: при 3 железных пластинах крафт ленты планирует
iron-gear-wheel → transport-belt, после чего в инвентаре 2 ленты и 0
пластин. При 6 пластинах максимум равен 2 крафтам (4 ленты), очередь содержит
gear, belt, gear, belt.
- Shift + левая кнопка на слоте игрока переносит весь стак в открытый инвентарь машины, а Shift + левая кнопка на слоте машины — весь стак обратно игроку.
- Shift + правая кнопка переносит половину стака.
- Для топлива сначала используется слот топлива, затем вход/хранилище; выходные слоты при быстром переносе не используются.
- Если в целевой инвентарь помещается только часть стака, переносится эта часть, остальное остаётся в исходном слоте.
- При одном открытом инвентаре игрока Shift сохраняет обычное поведение — другого инвентаря для переноса нет.
Проверено на сундуке: 20 пластин ушли из инвентаря игрока в сундук одним Shift-кликом и вернулись обратно Shift-кликом по слоту сундука.
- Прокрутка колеса приходит как
InputEventMouseButton, поэтому старый обработчик рецепта принимал её за левый клик и ставил крафт в очередь. _on_recipe_input()теперь реагирует только на левую и правую кнопки; колесо больше ничего не крафтит и может прокручивать список.
Проверено: колесо над рецептом оставляет очередь пустой, обычный левый клик по-прежнему ставит рецепт.
_accepts_input()дляmining-drillвозвращалfalse, поэтому ни соседний бур, ни манипулятор не могли положить уголь в его топливный слот._primary_inventory()для бура всегда выбирал OUTPUT. Теперь для входа топлива у burner-бура выбирается слот FUEL; электрический бур, как и раньше, топливо не принимает.- Это исправляет и прямой сброс добытого угля из одного burner mining drill в топливный слот соседнего, и подачу угля манипулятором.
Fuel capacity burner mining drill — 1 слот; при заполненном слоте бур, как и раньше, ищет конвейер/сундук/землю.
- В главном меню и меню паузы пункт Settings открывает отдельное окно.
- Sound volume управляет эффектами и рабочими звуками машин.
- Music volume управляет только фоновой музыкой; оба значения
применяются сразу и сохраняются в
user://settings.json. - Simplify terrain at zoom ≤ задаёт порог упрощения тайлов. Это значение
в тех же единицах, что и зум камеры: при зуме меньше или равном порогу
отдельные тайлы заменяются кэшированными упрощёнными чанками. По умолчанию
0.70×;0.20×включает упрощение только при максимальном отдалении. - Настройки сохраняются между запусками.
Проверено в редакторе: изменение громкости сразу меняет
FactorioAudio.master_volume / music_volume, порог — WorldRenderer.ground_lod_threshold,
а файл настроек создаётся в user://settings.json.
pumping_speedв Factorio 2.0 задан в единицах за тик (20 у offshore pump), а код умножал его наTICK_DELTAи получал только 20 воды/с.- Один boiler забирает 1 воду/тик (60/с), поэтому воды не хватало, котёл не выдавал пар, турбины стояли, а электрические буры мигали «No power».
- Теперь pump добавляет
pumping_speedза тик. На survival: pump выдаёт 1200/с, boiler заполнен, обе турбины дают по 900 kW, суммарно 1.8 MW при потреблении 180 kW; satisfaction буров 100%, уведомлений No power нет.
- Провода между опорами теперь рисуются не одной прямой, а сегментами
провисающей кривой (та же форма, что у Factorio). Сегменты используют
реальные точки крепления
connection_points. - Точки крепления больше не поворачиваются вместе с сущностью: у small pole, повёрнутого на запад, верхняя точка превращалась в боковую у основания, и провод шёл низом. Теперь выбирается самая верхняя copper-точка, поэтому провод висит на верхушке опоры.
- Если в руках электрическая опора, до установки показываются провода до
всех соседних опор в радиусе
maximum_wire_distance:- зелёные — опора помещается и соединение будет создано;
- красные — место занято/недопустимо;
- синие — опора ставится призраком (предмета нет).
- Предпросмотр использует те же
wire_curve()иwire_anchor(), что и настоящие провода, поэтому картинка совпадает с итоговой сетью.
- Иконка инженера и иконка брони в панели оружия теперь работают как слот брони: клик надевает лучший доступный костюм из инвентаря (или костюм, который держится на курсоре), затем открывает его сетку оборудования.
- Костюм меняется только на более высокий по
order, поэтому случайный клик не снимет текущую хорошую броню. modular-armorдаёт сетку 5×5,power-armor— 6×8,power-armor-mk2— 10×10; сетка открывается сразу после экипировки.- Старый костюм возвращается в инвентарь, как в Factorio.
Проверено: клик по иконке брони надевает modular-armor и открывает сетку
5×5; после добавления power-armor-mk2 следующий клик меняет костюм на него
и открывает сетку 10×10.
- Карта показывает только разведанные чанки. Просто сгенерированный
чанк ещё не виден: без игрока/радара на его месте рисуется размытый
спутниковый шум. Клик по разведанному месту закрывает карту и переносит
обычную камеру туда, где видны все механизмы, трубы, провода и анимации;
вернуться к инженеру можно кнопкой Return to engineer или
Home. - Вокруг инженера автоматически прогружается небольшой радиус (2 чанка), чтобы обычная игра не показывала чёрные дыры.
- Остальные чанки прогружаются только вокруг работающего радара: игра берёт
max_distance_of_nearby_sector_revealedи ставит чанки в очередь загрузки (по 3 чанка за кадр). Без радара/питания на месте остаются шум и клик сообщает об отсутствии спутниковых данных. - Рендерер и
MapChunksбольше не генерируют мир от простого взгляда на карту или камеру; карта не создала ни одного чанка (проверено:chunk_countдо/после открытия и панорамирования не изменился). GameWorld.revealed_chunksхранится в сохранении; старые сохранения без этого поля начинают с шума вне стартовой области.
Проверено: удалённый вызов рендерера не создаёт чанк; очередь загрузчика
создаёт его; клик по загруженному месту на карте переносит камеру в
(100,100) и закрывает карту.
- Текущий индекс цепочки Objective пишется в сохранение (
objectives.current) и восстанавливается при загрузке. Старые сохранения без этого поля начинают цепочку с начала. - Новая игра сбрасывает цепочку на первый шаг, а не наследует прогресс предыдущей.
- В Settings → Tutorial появился чекбокс Show tutorial objectives.
Когда он снят:
- панель Objective скрыта;
- проверки цепочки не выполняются;
- уведомления и звук нового objective не появляются.
- Прогресс при этом не стирается: после включения галочки цепочка продолжит с сохранённого шага.
Проверено: сохранение с objectives.current = 13 после загрузки оставляет
индекс 13; снятие галочки скрывает Objective, включение возвращает панель.
- Список
Technology listтеперь сначала показывает все технологии, которые можно начать исследовать прямо сейчас (предпосылки выполнены, технология не исследована и не является trigger-технологией). - Внутри этой группы сохраняется дешёвый порядок
Research.available_technologies(); остальные технологии идут ниже в исходном порядке прототипов. - Список пересобирается при
availability_changed, поэтому после завершения исследования новые доступные карточки сразу поднимаются наверх.
Проверено: при доступных automation, gun-turret, stone-wall, lamp
и других первые карточки списка имеют is_available == true.
- Панель оружия теперь прижата к самому низу левого угла (отступ 12 px).
- Objective находится непосредственно над ней, с отступом 12 px.
- При отключении обучения скрывается только Objective; панель оружия остаётся на месте и больше не выглядит висящей в воздухе.
Проверено: при выключенном обучении weapon panel имеет позицию (12, 776)
при окне 900 px, то есть стоит у нижнего края.
- Камера теперь действительно отпускается кликом по миникарте/алерту и
возвращается на инженера по
Home. РаньшеPlayerкаждый кадр заново привязывал её к персонажу, поэтому «отпустить» камеру было невозможно. - Второй
KEY_Eв обработчике был недостижим — окно экипировки теперь наG. close_window()окон больше не уничтожает предмет на курсоре.- Загрузка сохранения нормализует быструю панель до 20 слотов.
Проверялось в запущенной из редактора игре (project_run), с управлением
через редакторский MCP:
- появление персонажа и корректная лента конвейеров —
tmp_preview/live3.png,tmp_preview/live.png,tmp_preview/final_game.png(крупно —tmp_preview/final_char_crop.png); - превью постройки и поворот
R(восток/юг) —tmp_preview/preview_compare.png; - миникарта с меткой игрока —
tmp_preview/minimap_crop.png; - карта с маркером и кэшем чанков —
tmp_preview/map_live2.png; - титульный экран —
tmp_preview/menu_live.png; - 2 ряда быстрой панели, назначение предмета в слот 12, постановка предмета из инвентаря, сохранение/загрузка — проверены вызовами игрового API и вводом клавиш в запущенной игре;
- FPS и тайминги —
Performance/Timeиз работающей игры; - безопасный спавн — 3 новые игры подряд, в каждой клетка 3×3 вокруг инженера
свободна и
blocked()= false; - постановка при открытом инвентаре — реальный
Viewport.push_input()по миру; - рука манипулятора, подсветка и ночной фонарик —
tmp_preview/factory3.png,tmp_preview/inserter_motion2.png,tmp_preview/inserter_hover_crop.png,tmp_preview/night_final.png; - анимация ленты и предметы поверх неё —
tmp_preview/belt_compare.png(два кадра подряд); - правила постановки: бур на пустой земле — отказ, бур на руде — успех,
печь на воде — отказ, landfill на
water-shallow→ тайлlandfill, скриншотtmp_preview/landfill.png; Wбольше не включает провод: после движенияwire_colourпуст, а печь ставится; кнопки провода стоят справа от панели (скриншотtmp_preview/wirebar_active2.png), активная подсвечена, а клик по печи при выключенном проводе открывает её окно;- проходимость и ленты:
blocked()= false на клетках ленты, столба и манипулятора; стоя на жёлтой ленте, инженер проехал ~1.2 тайла за 40 кадров на восток;PowerNetwork.connections()даёт 2 соединения у стартовых столбов, медные провода и радиус видны наtmp_preview/copper_wires_crop.png; - буры: стартовые
direction = 8,Rменяет на 12, выходная клетка пересчитывается; прямой выход на ленту кладёт руду вBeltSim(tmp_preview/drill_output.png); бур с пустой клеткой впереди создаётitem-entity(item-on-ground,iron-ore), который поднимается кликом; Qпо печи без предмета в инвентаре даётcursor_itemсcount = 0, а клик создаёт призрак; пустая ячейка панели открывает окно выбора (tmp_preview/item_picker2_crop.png, 15 предметов на старте);- значок «нет электричества» виден над незапитанным вставщиком
(
tmp_preview/nopower_crop.png); - наземный предмет поднимается ПКМ (
iron-ore+3,item-entityисчез); - анимация электрического бура: голова и выходной лоток меняют кадр
(
tmp_preview/drill_anim_a_crop.pngи..._b_crop.png); Qс 5 лентами в руках очищает руку даже при курсоре вне мира;Qс пустой рукой над печью берёт печь;- инвентарь, крафт и выбор предмета сортируются по
item_sort_key(); в инвентаре первыми идут логистика/производство, затем сырьё и промежуточные продукты (tmp_preview/inventory_sorted.png); - деревья и камни спавнятся на суше: >1500 деревьев, ~90 камней, 0 на воде;
стартовая фабрика — 33 объекта;
tmp_preview/forest.png; - LOD при отдалении: zoom 0.5 — 8 405 → ~80 предметов, FPS 21 → 60;
zoom 0.1 — 60 FPS, тик продолжает идти (
tmp_preview/zoom_lod.png).
- Для запуска нужна установленная Factorio: без её
data/не будет ни прототипов, ни спрайтов. - Неисследованные чанки на полной карте тёмные — специально, чтобы открытие карты не генерировало полмира.
- Загрязнение на карте рисуется крупными блоками (шаг 8 клеток) — это компромисс между скоростью и точностью.
- Офлайн-инструменты
tools/preview_ui.pyиtools/preview_draw.py, а также CLI-флаги (--screenshot,--dump-draw,--dump-ui,--minimap-png) остались в проекте, но в этот раз ими не пользовались: проверка велась только через редактор.
Код проекта — самостоятельный. Все имена, спрайты, звуки и таблицы Factorio принадлежат Wube Software; они не распространяются вместе с репозиторием и читаются из установленной игры.




