Block Placer placement context issue fix. Fixed compatibility to Forge >=37.0.82. Update lang ru_ru (PR#191, Smollet777).

This commit is contained in:
stfwi 2021-10-03 19:17:48 +02:00
parent 80abccc6cc
commit 8a26de7c2b
11 changed files with 58 additions and 47 deletions

View file

@ -41,13 +41,6 @@ minecraft {
property 'forge.logging.console.level', 'info'
mods { engineersdecor { source sourceSets.main } }
}
// data {
// workingDirectory project.file('run')
// property 'forge.logging.markers', '' // SCAN,REGISTRIES,REGISTRYDUMP
// property 'forge.logging.console.level', 'info'
// args '--mod', 'engineersdecor', '--all', '--output', file('src/generated/resources/')
// mods { engineersdecor { source sourceSets.main } }
// }
}
}
@ -76,7 +69,6 @@ jar {
}
}
sourceSets.main.resources { srcDir 'src/generated/resources' } // Include datagen resources
jar.finalizedBy('reobfJar')
// import net.minecraftforge.gradle.common.task.SignJar

View file

@ -2,6 +2,6 @@
org.gradle.daemon=false
org.gradle.jvmargs=-Xmx8G
version_minecraft=1.17.1
version_forge_minecraft=1.17.1-37.0.51
version_jei=1.17.1:8.0.0.15
version_engineersdecor=1.1.17
version_forge_minecraft=1.17.1-37.0.104
version_jei=1.17.1:8.1.0.31
version_engineersdecor=1.1.18

View file

@ -1,6 +1,7 @@
{
"homepage": "https://www.curseforge.com/minecraft/mc-mods/engineers-decor/",
"1.17.1": {
"1.1.18": "[F] Block Placer placement context issue fix.\n[F] Fixed compatibility to Forge >=37.0.82.\n[M] Update lang ru_ru (PR#191, Smollet777).",
"1.1.17": "[R] Initial 1.17.1 release.",
"1.1.17-b5": "[F] Refurbished Mineral Melter code.\n[F] Fixed Block Placer item insertion.",
"1.1.17-b4": "[F] Config event update fixed.\n[F] Milker fluid tank handling issue fixed.\n[F] Device collision shapes adapted to allow attaching levers.",
@ -45,7 +46,7 @@
"1.1.2-b1": "[U] Ported to MC1.16.2."
},
"promos": {
"1.17.1-recommended": "1.1.17",
"1.17.1-latest": "1.1.17"
"1.17.1-recommended": "1.1.18",
"1.17.1-latest": "1.1.18"
}
}

View file

@ -1,7 +1,7 @@
## Engineer's Decor (MC1.16.x)
## Engineer's Decor (MC1.17.x)
Mod sources for Minecraft version 1.16.x.
Mod sources for Minecraft version 1.17.x.
- Description, credits, and features: Please see the readme in the repository root.
@ -11,6 +11,10 @@ Mod sources for Minecraft version 1.16.x.
## Version history
- v1.1.18 [F] Block Placer placement context issue fix.
[F] Fixed compatibility to Forge >=37.0.82.
[M] Update lang ru_ru (PR#191, Smollet777).
- v1.1.17 [R] Initial 1.17.1 release.
- v1.1.17-b5 [F] Refurbished Mineral Melter code.

View file

@ -90,7 +90,6 @@ public class ModContent
}
}
//--------------------------------------------------------------------------------------------------------------------
// Blocks
//--------------------------------------------------------------------------------------------------------------------

View file

@ -461,14 +461,11 @@ public class EdPlacer
if(((BlockItem)item).place(use_context) != InteractionResult.FAIL) {
SoundType stype = block.getSoundType(placement_state, level, worldPosition, null);
if(stype != null) level.playSound(null, placement_pos, stype.getPlaceSound(), SoundSource.BLOCKS, stype.getVolume()*0.6f, stype.getPitch());
} else if(block instanceof IPlantable) {
} else {
if(level.setBlock(placement_pos, placement_state, 1|2|8)) {
SoundType stype = block.getSoundType(placement_state, level, worldPosition, null);
if(stype != null) level.playSound(null, placement_pos, stype.getPlaceSound(), SoundSource.BLOCKS, stype.getVolume()*0.6f, stype.getPitch());
}
} else {
Auxiliaries.logDebug("Placer spit: try-place and planting failed for item " + item.getRegistryName().toString());
return spit_out(facing);
}
} else {
if(level.setBlock(placement_pos, placement_state, 1|2|8)) {
@ -484,7 +481,11 @@ public class EdPlacer
// A hard crash should not be fired here, instead spit out the item to indicated that this
// block is not compatible.
ModEngineersDecor.logger().error("Exception while trying to place " + ((block==null)?(""):(""+block)) + ", spitting out. Exception is: " + e);
level.removeBlock(placement_pos, false);
try {
level.removeBlock(placement_pos, false);
} catch(Throwable e1) {
ModEngineersDecor.logger().error("Exception while removing failed block placement " + ((block==null)?(""):(""+block)) + ", spitting out. Exception is: " + e1);
}
return spit_out(facing, true);
}
}

View file

@ -1,11 +1,11 @@
/*
* @file BlockDecorHalfSlab.java
* @file SlabSliceBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Half slab ("slab slices") characteristics class. Actually
* it's now a quater slab, but who cares.
* it's now a quarter slab, but who cares.
*/
package wile.engineersdecor.libmc.blocks;

View file

@ -465,7 +465,7 @@ public class StandardBlocks
protected final Map<BlockState, VoxelShape> shapes;
protected final Map<BlockState, VoxelShape> collision_shapes;
public HorizontalFourWayWaterLoggable(long config, BlockBehaviour.Properties properties, AABB base_aabb, final AABB side_aabb, int railing_height_extension)
public HorizontalFourWayWaterLoggable(long config, BlockBehaviour.Properties properties, AABB base_aabb, final AABB side_aabb[], int railing_height_extension)
{
super(config, properties, base_aabb);
Map<BlockState, VoxelShape> build_shapes = new HashMap<>();
@ -473,10 +473,10 @@ public class StandardBlocks
for(BlockState state:getStateDefinition().getPossibleStates()) {
{
VoxelShape shape = ((base_aabb.getXsize()==0) || (base_aabb.getYsize()==0) || (base_aabb.getZsize()==0)) ? Shapes.empty() : Shapes.create(base_aabb);
if(state.getValue(NORTH)) shape = Shapes.joinUnoptimized(shape,Shapes.create(Auxiliaries.getRotatedAABB(side_aabb, Direction.NORTH, true)), BooleanOp.OR);
if(state.getValue(EAST)) shape = Shapes.joinUnoptimized(shape,Shapes.create(Auxiliaries.getRotatedAABB(side_aabb, Direction.EAST, true)), BooleanOp.OR);
if(state.getValue(SOUTH)) shape = Shapes.joinUnoptimized(shape,Shapes.create(Auxiliaries.getRotatedAABB(side_aabb, Direction.SOUTH, true)), BooleanOp.OR);
if(state.getValue(WEST)) shape = Shapes.joinUnoptimized(shape,Shapes.create(Auxiliaries.getRotatedAABB(side_aabb, Direction.WEST, true)), BooleanOp.OR);
if(state.getValue(NORTH)) shape = Shapes.joinUnoptimized(shape,Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(side_aabb, Direction.NORTH, true)), BooleanOp.OR);
if(state.getValue(EAST)) shape = Shapes.joinUnoptimized(shape,Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(side_aabb, Direction.EAST, true)), BooleanOp.OR);
if(state.getValue(SOUTH)) shape = Shapes.joinUnoptimized(shape,Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(side_aabb, Direction.SOUTH, true)), BooleanOp.OR);
if(state.getValue(WEST)) shape = Shapes.joinUnoptimized(shape,Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(side_aabb, Direction.WEST, true)), BooleanOp.OR);
if(shape.isEmpty()) shape = Shapes.block();
build_shapes.put(state.setValue(WATERLOGGED, false), shape);
build_shapes.put(state.setValue(WATERLOGGED, true), shape);
@ -484,10 +484,14 @@ public class StandardBlocks
{
// how the hack to extend a shape, these are the above with y+4px.
VoxelShape shape = ((base_aabb.getXsize()==0) || (base_aabb.getYsize()==0) || (base_aabb.getZsize()==0)) ? Shapes.empty() : Shapes.create(base_aabb);
if(state.getValue(NORTH)) shape = Shapes.joinUnoptimized(shape,Shapes.create(Auxiliaries.getRotatedAABB(side_aabb, Direction.NORTH, true).expandTowards(0, railing_height_extension, 0)), BooleanOp.OR);
if(state.getValue(EAST)) shape = Shapes.joinUnoptimized(shape,Shapes.create(Auxiliaries.getRotatedAABB(side_aabb, Direction.EAST, true).expandTowards(0, railing_height_extension, 0)), BooleanOp.OR);
if(state.getValue(SOUTH)) shape = Shapes.joinUnoptimized(shape,Shapes.create(Auxiliaries.getRotatedAABB(side_aabb, Direction.SOUTH, true).expandTowards(0, railing_height_extension, 0)), BooleanOp.OR);
if(state.getValue(WEST)) shape = Shapes.joinUnoptimized(shape,Shapes.create(Auxiliaries.getRotatedAABB(side_aabb, Direction.WEST, true).expandTowards(0, railing_height_extension, 0)), BooleanOp.OR);
if(state.getValue(NORTH)) shape = Shapes.joinUnoptimized(shape,Auxiliaries.getUnionShape(Auxiliaries.getMappedAABB(Auxiliaries.getRotatedAABB(side_aabb,
Direction.NORTH, true), bb->bb.expandTowards(0, railing_height_extension, 0))), BooleanOp.OR);
if(state.getValue(EAST)) shape = Shapes.joinUnoptimized(shape,Auxiliaries.getUnionShape(Auxiliaries.getMappedAABB(Auxiliaries.getRotatedAABB(side_aabb,
Direction.EAST, true), bb->bb.expandTowards(0, railing_height_extension, 0))), BooleanOp.OR);
if(state.getValue(SOUTH)) shape = Shapes.joinUnoptimized(shape,Auxiliaries.getUnionShape(Auxiliaries.getMappedAABB(Auxiliaries.getRotatedAABB(side_aabb,
Direction.SOUTH, true), bb->bb.expandTowards(0, railing_height_extension, 0))), BooleanOp.OR);
if(state.getValue(WEST)) shape = Shapes.joinUnoptimized(shape,Auxiliaries.getUnionShape(Auxiliaries.getMappedAABB(Auxiliaries.getRotatedAABB(side_aabb,
Direction.WEST, true), bb->bb.expandTowards(0, railing_height_extension, 0))), BooleanOp.OR);
if(shape.isEmpty()) shape = Shapes.block();
build_collision_shapes.put(state.setValue(WATERLOGGED, false), shape);
build_collision_shapes.put(state.setValue(WATERLOGGED, true), shape);
@ -498,6 +502,9 @@ public class StandardBlocks
registerDefaultState(super.defaultBlockState().setValue(NORTH, false).setValue(EAST, false).setValue(SOUTH, false).setValue(WEST, false).setValue(WATERLOGGED, false));
}
public HorizontalFourWayWaterLoggable(long config, BlockBehaviour.Properties properties, AABB base_aabb, final AABB side_aabb, int railing_height_extension)
{ this(config, properties, base_aabb, new AABB[]{side_aabb}, railing_height_extension); }
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
{ super.createBlockStateDefinition(builder); builder.add(NORTH,EAST,SOUTH,WEST); }

View file

@ -43,6 +43,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.UUID;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -373,6 +374,12 @@ public class Auxiliaries
return shape;
}
public static final AABB[] getMappedAABB(AABB[] bbs, Function<AABB,AABB> mapper) {
final AABB[] transformed = new AABB[bbs.length];
for(int i=0; i<bbs.length; ++i) transformed[i] = mapper.apply(bbs[i]);
return transformed;
}
public static final class BlockPosRange implements Iterable<BlockPos>
{
private final int x0, x1, y0, y1, z0, z1;

View file

@ -12,14 +12,14 @@ displayName="Engineer's Decor"
description="Adds cosmetic blocks for the engineer's workshop, factory and home."
authors="wilechaote"
credits="BluSunrize, Damien Hazard, malte0811, et al., the Forge Smiths."
updateJSONURL="https://raw.githubusercontent.com/stfwi/engineers-decor/1.16/meta/update.json"
updateJSONURL="https://raw.githubusercontent.com/stfwi/engineers-decor/1.17/meta/update.json"
displayURL="https://github.com/stfwi/engineers-decor/"
logoFile="logo.png"
[[dependencies.engineersdecor]]
modId="forge"
mandatory=true
versionRange="[37,)"
versionRange="[37.0.82,)"
ordering="NONE"
side="BOTH"

View file

@ -51,7 +51,7 @@
"engineersdecor.tooltip.slabpickup.help": "§rБыстрое поднятие щелчком ЛКМ, смотря вверх/вниз с этой плитой в руках.",
"block.engineersdecor.clinker_brick_block": "Клинкерный кирпич",
"block.engineersdecor.clinker_brick_block.help": "Кирпичный блок с вариациями текстуры, зависящими от положения.\nВыглядит темнее и интенсивнее, чем Кирпичный блок.",
"block.engineersdecor.clinker_brick_recessed": "Recessed Clinker Brick",
"block.engineersdecor.clinker_brick_recessed": "Утопленный клинкерный кирпич",
"block.engineersdecor.clinker_brick_sastor_corner_block": "Украшенный песчанником клинкерный кирпич",
"block.engineersdecor.clinker_brick_sastor_corner_block.help": "Угловое украшение для акцентирования клинкерных стен.\nРазмещайте, смотря горизонтально, чтобы создать орнамент угла здания.\nРазмещайте, глядя вверх/вниз для украшений оконных проёмов.\nСтиль соседних таких блоков копируется (привилегирует), чтобы облегчить стройку высоких зданий.",
"block.engineersdecor.clinker_brick_slab": "Клинкерная плита",
@ -64,8 +64,8 @@
"block.engineersdecor.clinker_brick_stained_stairs.help": "Выглядят немного темнее и интенсивнее, чем Кирпичный блок. Имеют более заметные следы грязи или пятен.",
"block.engineersdecor.clinker_brick_stairs": "Клинкерные кирпичные ступеньки",
"block.engineersdecor.clinker_brick_stairs.help": "По цвету выглядят немного темнее и интенсивнее, чем Кирпичный блок",
"block.engineersdecor.clinker_brick_vertical_slab_structured": "Structured Vertical Clinker Slab",
"block.engineersdecor.clinker_brick_vertically_slit": "Vertically Slit Clinker Bricks",
"block.engineersdecor.clinker_brick_vertical_slab_structured": "Структурированная вертикальная клинкерная плита",
"block.engineersdecor.clinker_brick_vertically_slit": "Клинкерный кирпич с вертикальным разрезом",
"block.engineersdecor.clinker_brick_wall": "Клинкерная кирпичная стена",
"block.engineersdecor.clinker_brick_wall.help": "Обыкновенная клинкерная кирпичная стена.",
"block.engineersdecor.dark_shingle_roof": "Тёмная керамическая черепица",
@ -78,17 +78,17 @@
"block.engineersdecor.dark_shingle_roof_skylight": "Тёмная керамическая черепица с люкарной",
"block.engineersdecor.dark_shingle_roof_slab": "Тёмная керамическая черепичная плита",
"block.engineersdecor.dark_shingle_roof_slabslice": "Тёмная керамическая черепичная пластина",
"block.engineersdecor.dark_shingle_roof_wireconduit": "Тёмная керамическая черепица Wire Conduit",
"block.engineersdecor.dark_shingle_roof_wireconduit": "Проводник из тёмной керамической черепицы",
"block.engineersdecor.dense_grit_dirt_block": "Плотная зернистая земля",
"block.engineersdecor.dense_grit_dirt_block.help": "Сжатый земляной грунт с простой текстурой с небольшими трещинами.\nМожно найти на тропинках или старых дворовых площадках.\nДля акцентирования можно мешать землю и каменистую землю.\nВариации текстуры зависят от позиции. Предотвращает распространение травы.",
"block.engineersdecor.dense_grit_sand_block": "Плотный зернистый песок",
"block.engineersdecor.dense_grit_sand_block.help": "A compressed sandy soil mixed with stone grid, as known from machine and storage yards.\nFlowers don't really grow on that. Position dependent texture variations.",
"block.engineersdecor.dense_grit_sand_block.help": "A compressed sandy soil mixed with stone grid, as known from machine and storage yards.\nНа таком цветы не растут. Вариации текстуры зависят от позиции.",
"block.engineersdecor.factory_dropper": "Фабричный выбрасыватель",
"block.engineersdecor.factory_dropper.help": "Выбрасыватель подходит для продвинутой автоматизации производства.\nИмеет 12 выборочных слотов. Сила броска, угол, размер стопки и задержка настраиваются в GUI.\n3 слота сравнения стека с логическим И или ИЛИ могут использоваться в качестве внутреннего источника запуска.\nВнутренний триггер может быть И или ИЛИ с внешним триггерным сигналом красного камня.\nТриггерные кнопки симуляции для тестирования. Предварительно открывает дверцу\nзатвора, когда выполняются условия внутреннего запуска. Сбрасывает все соответствующие\nстеки одновременно. Нажмите на все элементы в GUI, чтобы увидеть, как это работает.",
"block.engineersdecor.factory_dropper.tooltips.direction": "Направление выпадения x/y (-45° до +45°)",
"block.engineersdecor.factory_dropper.tooltips.dropcount": "Размер выпадающего стека (1 до 32 предметов)",
"block.engineersdecor.factory_dropper.tooltips.externgate": "External signal combination logic (AND or OR)§r\nAND: Drop only on an external signal.\nOR: Drop filter matches automatically, non-matching items on external trigger.",
"block.engineersdecor.factory_dropper.tooltips.filtergate": "Filter combination gate logic (AND or OR)§r\nAND: Drop the stacks as specified in the filters when ALL filters are green.\nOR: Drop the stack of each individual filter when green.",
"block.engineersdecor.factory_dropper.tooltips.externgate": "Логика комбинаций внешних сигналов (И или ИЛИ)§r\nИ: выпадение только по внешнему сигналу.\nИЛИ: фильтр выпадения соответствует автоматически, несовпадающие предметы на внешнем воздействии.",
"block.engineersdecor.factory_dropper.tooltips.filtergate": "Логика комбинаций фильтров (И или ИЛИ)§r\nИ: сбросить стаки, как указано в фильтрах когда ВСЕ фильтры зелёные.\nИЛИ: сбросить стак каждого отдельного фильтра, когда зеленый.",
"block.engineersdecor.factory_dropper.tooltips.period": "Задержка выпадения (1сек до 10сек)",
"block.engineersdecor.factory_dropper.tooltips.rssignal": "Индикатор внешнего редстоун сигнала (клик для тестового триггера).",
"block.engineersdecor.factory_dropper.tooltips.triggermode": "Режим триггера (импульсный/непрерывный)§r\nИмпульсный: одно выпадение на каждый ВЫКЛ->ВКЛ внешнего сигнала.\nНепрерывный: выбрасиывает пока внешний сигнал ВКЛ.",
@ -108,7 +108,7 @@
"block.engineersdecor.factory_placer.tooltips.rssignal": "Индикатор внешнего редстоун сигнала",
"block.engineersdecor.factory_placer.tooltips.triggermode": "Режим триггера §8(импульсный/непрерывный)",
"block.engineersdecor.fluid_barrel": "Бочка для жидкости",
"block.engineersdecor.fluid_barrel.help": "Simple barrel for storing liquids. Has a small built-in pressure-tube based level gauge.\nDefault placement is standing, sneak-place to select a specific direction, transfers fluids in tanks below when standing. Supports fluid extraction/insertion as item and Comparator output.",
"block.engineersdecor.fluid_barrel.help": "Простая бочка для хранения жидкостей. Имеет небольшой встроенный манометр на основе напорной трубки.\nРазмещения по умолчанию - стоя, в присяди - выбрать конкретное направление. Сливает жидкости в резервуары снизу. Поддерживает извлечение/введение жидкости и сигнал компаратора.",
"block.engineersdecor.fluid_barrel.status": "Наполненная бочка: §6%1$s§r / %2$s mB §6%3$s§r",
"block.engineersdecor.fluid_barrel.status.empty": "Пустая бочка: §6%1$s§r / %2$s mB",
"block.engineersdecor.fluid_barrel.status.tip": "§6%1$s§r / %2$s mB §6%3$s§r",
@ -146,7 +146,7 @@
"block.engineersdecor.metal_crafting_table.tooltips.fromplayer": "Переместить предметы из инвентаря игрока",
"block.engineersdecor.metal_crafting_table.tooltips.fromstorage": "Переместить предметы из хранилища",
"block.engineersdecor.metal_crafting_table.tooltips.next": "Следующий рецепт истории крафта",
"block.engineersdecor.metal_crafting_table.tooltips.nextcollisionrecipe": "Show next colliding recipe",
"block.engineersdecor.metal_crafting_table.tooltips.nextcollisionrecipe": "Показать следующий рецепт",
"block.engineersdecor.metal_crafting_table.tooltips.prev": "Предыдущий рецепт истории крафта",
"block.engineersdecor.metal_crafting_table.tooltips.toplayer": "Очистить сетку в инвентарь игрока",
"block.engineersdecor.metal_crafting_table.tooltips.tostorage": "Очистить сетку в хранилище",
@ -209,7 +209,7 @@
"block.engineersdecor.small_electrical_furnace": "Компактная конвейерная электрическая печь",
"block.engineersdecor.small_electrical_furnace.help": "Компактная конвейерная печь в металлическом корпусе. Автоматически принимает предметы со стороны ввода и складывает в инвентарь со стороны вывода. Предметы можно помещать/забирать со всех сторон с помощью воронок. Без проблем пропускает элементы, которые нельзя выплавить или приготовить. Чуть более энергоэффективная и быстрая, чем утеплённая булыжная печь. Транспортировка работает стеками. Механизм требует мало энергии.",
"block.engineersdecor.small_electrical_furnace.tooltips.auxslot": "Вспомогательный слот\n§8Поместите сюда воронку, чтобы включить автоматическую подачу со стороны входа.",
"block.engineersdecor.small_electrical_furnace.tooltips.capacitors": "SOC %1$s%%, %2$srf/t",
"block.engineersdecor.small_electrical_furnace.tooltips.capacitors": "SOC %1$s%%, %2$srf/тик",
"block.engineersdecor.small_electrical_furnace.tooltips.speed": "Выбор скорости плавки §8(ВЫКЛ/I/II/III)",
"block.engineersdecor.small_fluid_funnel": "Малая воронка для сбора жидкости",
"block.engineersdecor.small_fluid_funnel.help": "Собирает жидкости над ним. Имеет внутренний бак на три ведра. Прослеживает текучие жидкости к соседним блокам источника. Жидкость может быть получена с помощью систем передачи жидкости или ведра. Заполняет только резервуары ниже (сила гравитации). Совместим с ванильным источником бесконечной воды.",
@ -290,7 +290,7 @@
"block.engineersdecor.treated_wood_windowsill": "Обработанный деревянный подоконник",
"block.engineersdecor.treated_wood_windowsill.help": "Простое оформление окон.",
"item.engineersdecor.metal_bar": "Металлический брусок",
"engineersdecor.book.landing_text": "Tip in advance: To get a short tooltip help text for a block or device, press the CONTRTOL and SHIFT keys at the same time while hovering. That way you do not need to carry this heavy manual with you all the time. The more detailed descriptions in this book are helpful if the features are new for you, or if you like to read up about background aspects.",
"engineersdecor.book.name": "Engineer's Decor",
"engineersdecor.book.subtitle": "Reference manual"
"engineersdecor.book.landing_text": "Совет заранее: Чтобы получить краткую подсказку для блока или устройства, зажмите клавиши CONTROL и SHIFT при наведении мышкой. Таким образом, Вам не нужно постоянно носить с собой это тяжелое руководство. Более подробные описания в этой книге будут полезны, если функции в новинку, или если хотите узнать об основных аспектах.",
"engineersdecor.book.name": "Инженерный декор",
"engineersdecor.book.subtitle": "Справочное руководство"
}