Иконка ресурса

Реализация проводов

Версия(и) Minecraft
1.12.2
@Deprecated
Приветствую! Я тут недавно закончил делать провода и теперь хочу поделиться с Вами тем, что у меня получилось. Не просто расскажу, а попытаюсь объяснить, как это работает.

Итак, давайте порассуждаем. Каким образом можно реализовать провода?

Иметь для каждого провода тайл и каждую секунду передавать енергию?

Не… Начнется лютый лагодром и каким образом понять куда доставлять енергию.

Лучше с помощью алгоритма поиска в ширину(Почему он? Он проходится по всем вершинам графа, то есть он проверит все подключенные провода) проверять все подключенные проводами «поставщики» енергии и «потребители». Всем «поставщикам» енергии выдавать список подключенных «потребителей». Остальное, я думаю вы подчеркнете из прокомментированного класса провода. Там к слову есть и визуальная реализация проводов.

Java:
public class Wire extends Block{
    //Каждое свойство отвечает за то, подключен ли к этой стороне провод или другой участник цепи(true/false)
    public static final PropertyBool DOWN = PropertyBool.create("down");
    public static final PropertyBool UP = PropertyBool.create("up");
    public static final PropertyBool NORTH = PropertyBool.create("north");
    public static final PropertyBool SOUTH = PropertyBool.create("south");
    public static final PropertyBool WEST = PropertyBool.create("west");
    public static final PropertyBool EAST = PropertyBool.create("east");

    public Wire() {
        super(Material.CIRCUITS);
        this.setRegistryName("wire");
        this.setUnlocalizedName("wire");
        this.setCreativeTab(CommonProxy.redstone_things_tab);
    }
    //Метод "строящий" сеть, передаем в него мир и стартовую позицию
    public ArrayList<BlockPos> buildNetwork(IBlockAccess worldIn, BlockPos start) {
        HashSet<BlockPos> checked = new HashSet<>();//список проверенных блоков
        ArrayList<BlockPos> generators = new ArrayList<>();//список генераторов, которые будут найдены в сети
        ArrayList<BlockPos> storages = new ArrayList<>();//список хранилищ, которые будут найдены в сети
        ArrayDeque<BlockPos> queue = new ArrayDeque<>(100);//Очередь - это особенность реализации алгоритма поиска в ширину.

        queue.offer(start);//Добавляем стартовую позицию в очередь
        checked.add(start);//И сразу добавляем в проверенные
        while (!queue.isEmpty()) {//Выполняем пока очередь не пуста
            BlockPos nPos = queue.poll();//Этот метод возвращает объект из головы очереди и сразу его удаляет.
            //Проверяем не генератор ли стартовая позиция.
            TileEntity tile = worldIn.getTileEntity(nPos);
            if(tile != null) {
                if(tile.hasCapability(EnergyGeneratorCapability.ENERGY_GENERATOR, null)) {// У меня свои капы на генератор и хранилище
                    generators.add(nPos);
                }
            }
            for (EnumFacing face : EnumFacing.VALUES) {//Циклом прогоняемся по всем возможным "направлением"(не знаю как лучше перевести
                BlockPos child = nPos.offset(face);//эта функция возвращает позицию со сдвигом в данном направлении(+1 в этом направлении)
                TileEntity tileEntity = worldIn.getTileEntity(child);//получаем тайл
                if(tileEntity != null) {
                    //проверяем, что это, генератор или хранилище
                    if(tileEntity.hasCapability(EnergyGeneratorCapability.ENERGY_GENERATOR, null)) {
                        generators.add(child); // и добовлеям их в соответственные списки
                    }
                    if(tileEntity.hasCapability(EnergyStorageCapability.ENERGY_STORAGE, null)) {
                        storages.add(child);// и добовлеям их в соответственные списки
                    }
                }
                //Если в списке проверенных нету этой позиции и блок на этой позиции - это провод, добавляем в список
                //проверенных + в очередь, чтобы с этой позиции проверить уже другие блоки
                if(!checked.contains(child) && worldIn.getBlockState(child).getBlock() instanceof Wire) {
                    checked.add(child);
                    queue.addLast(child);//Добавляем в низ очереди
                }
            }
        }

        if(!generators.isEmpty()) {//Если есть в сети  генераторы
            for (int i = 0; i < generators.size(); i++) {//прогоняемся по ним циклом
                BlockPos gPos = generators.get(i);
                TileEntity tileEntity = worldIn.getTileEntity(gPos);
                if(tileEntity != null && tileEntity instanceof GeneratorTile) {
                    //Проверяем, что они наследают спец класс GeneratorTile, нижке под спойлером есть этот класс
                    GeneratorTile generator = (GeneratorTile) tileEntity;
                    generator.setStorages(storages);//устонавливаем в этот генератор список найденных в сети хранилищ
                }
            }
        }
        return generators;
    }

    @Override
    public void onBlockDestroyedByPlayer(World worldIn, BlockPos pos, IBlockState state) {
        // Когда блок сломали, нам нужно всем проводам вокруг себя дать комманду перестроить сеть.
        // Ведь сломав провод мы можем разорвать соединение
        for (EnumFacing face : EnumFacing.VALUES) {
            BlockPos p = pos.offset(face);
            TileEntity tileEntity = worldIn.getTileEntity(p);
            if(tileEntity != null && tileEntity instanceof GeneratorTile) {
                //Даже если это генератор, мы перестраиваем сеть с его позиции, ведь
                //генератор может быть подключен всего одним кабелем
                buildNetwork(worldIn, p);
            }
            if(worldIn.getBlockState(p).getBlock() instanceof Wire) {
                //Заставляем провода в округе перестроить сеть
                Wire wire = (Wire) worldIn.getBlockState(p).getBlock();
                wire.buildNetwork(worldIn, p);
            }
        }
        super.onBlockDestroyedByPlayer(worldIn, pos, state);
    }

    @Override
    public void onBlockAdded(World worldIn, BlockPos pos, IBlockState state) {
        //Когда провод установлен нам нужно "построить сеть"
        this.buildNetwork(worldIn, pos);
        super.onBlockAdded(worldIn, pos, state);
    }

    @Override
    public void neighborChanged(IBlockState state, World worldIn, BlockPos pos, Block blockIn, BlockPos fromPos) {
        // Это даст нам возможность добавлять хранилища/генераторы если они были поставлены рядом
        this.buildNetwork(worldIn, pos);
        super.neighborChanged(state, worldIn, pos, blockIn, fromPos);
    }
    //Это метод нужен для проверки можем ли мы текстуркой "соедениться" с другим блоком
    public boolean canConnectTo(IBlockAccess world, BlockPos pos, EnumFacing facing){
        BlockPos child = pos.offset(facing);
        TileEntity tileEntity = world.getTileEntity(child);
        if(tileEntity != null) {
            if(tileEntity.hasCapability(EnergyStorageCapability.ENERGY_STORAGE, null)) {
                //если генератор - да
                return true;
            }
            if(tileEntity.hasCapability(EnergyGeneratorCapability.ENERGY_GENERATOR, null)) {
                //если хранилище - да
                return true;
            }
        }
        if(world.getBlockState(child).getBlock() instanceof Wire) {
            //если провод - да
            return true;
        }
        return false;
    }


    @Override
    public void addCollisionBoxToList(IBlockState state, World world, BlockPos pos, AxisAlignedBB entityBox,
            List<AxisAlignedBB> collidingBoxes, Entity entityIn, boolean isActualState) {
        //В зависимости от того, какие стороны подключены, выставляем определенную коллизию
        if (canConnectTo(world, pos, EnumFacing.UP)) {
            addCollisionBoxToList(pos, entityBox, collidingBoxes,
                    new AxisAlignedBB(0.377, 0, 0.377, 0.623D, 0.623D, 0.623D));
        }
        if (canConnectTo(world, pos, EnumFacing.UP)) {
            addCollisionBoxToList(pos, entityBox, collidingBoxes,
                    new AxisAlignedBB(0.377, 0.377, 0.377, 0.623D, 1, 0.623D));
        }
        if (canConnectTo(world, pos, EnumFacing.NORTH)) {
            addCollisionBoxToList(pos, entityBox, collidingBoxes,
                    new AxisAlignedBB(0.377, 0.377, 0, 0.623D, 0.623D, 0.623D));
        }
        if (canConnectTo(world, pos, EnumFacing.SOUTH)) {
            addCollisionBoxToList(pos, entityBox, collidingBoxes,
                    new AxisAlignedBB(0.377, 0.377, 0.377, 0.623D, 0.623D, 1));
        }
        if (canConnectTo(world, pos, EnumFacing.EAST)) {
            addCollisionBoxToList(pos, entityBox, collidingBoxes,
                    new AxisAlignedBB(0.377, 0.377, 0.377, 1, 0.623D, 0.623D));
        }
        if (canConnectTo(world, pos, EnumFacing.WEST)) {
            addCollisionBoxToList(pos, entityBox, collidingBoxes,
                    new AxisAlignedBB(0, 0.377, 0.377, 0.623D, 0.623D, 0.623D));
        }
    }

    @Override
    public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess world, BlockPos pos) {
        // Тоже самое, что и с коллизией
        double[] sidebound = new double[6];
        sidebound[0]=sidebound[1]=sidebound[2]=0.377D;
        sidebound[3]=sidebound[4]=sidebound[5]=0.6231D;

        if (canConnectTo(world, pos, EnumFacing.DOWN) == true) {
            sidebound[1]=0;
        }
        if (canConnectTo(world, pos, EnumFacing.UP) == true) {
            sidebound[4]=1;
        }
        if (canConnectTo(world, pos, EnumFacing.NORTH) == true) {
            sidebound[2]=0;
        }
        if (canConnectTo(world, pos, EnumFacing.SOUTH) == true) {
            sidebound[5]=1;
        }
        if (canConnectTo(world, pos, EnumFacing.WEST) == true) {
            sidebound[0]=0;
        }
        if (canConnectTo(world, pos, EnumFacing.EAST) == true) {
            sidebound[3]=1;
        }

        return new AxisAlignedBB(sidebound[0], sidebound[1], sidebound[2], sidebound[3], sidebound[4], sidebound[5]);
    }

    @Override
    protected BlockStateContainer createBlockState() {
        //Создаем контейнер для наших "свойств"
        return new BlockStateContainer(this, UP, DOWN, NORTH, SOUTH, WEST, EAST);
    }

    @Override
    public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos pos) {
        //Пользуясь методом просто устанавливаем true/false свойствам
        // С помощью этого мы будем рендерить(или нет) частичку кабеля с определенной стороны.
        return state
        .withProperty(DOWN, canConnectTo(world, pos, EnumFacing.DOWN))
        .withProperty(UP, canConnectTo(world, pos, EnumFacing.UP))
        .withProperty(NORTH, canConnectTo(world, pos, EnumFacing.NORTH))
        .withProperty(SOUTH, canConnectTo(world, pos, EnumFacing.SOUTH))
        .withProperty(WEST, canConnectTo(world, pos, EnumFacing.WEST))
        .withProperty(EAST, canConnectTo(world, pos, EnumFacing.EAST));
    }

    @Override
    public int getMetaFromState(IBlockState state) {
        return 0;
    }

    @Override
    public boolean isOpaqueCube(IBlockState state) {
        return false;
    }

    @Override
    public boolean isFullCube(IBlockState state) {
        return false;
    }

}

Java:
public class GeneratorTile extends TileEntity{
    public ArrayList<BlockPos> storages = new ArrayList<>();

    public void setStorages(ArrayList<BlockPos> storages) {
        this.storages = storages;
    }

    @Override
    public NBTTagCompound writeToNBT(NBTTagCompound compound) {
        NBTTagList list = new NBTTagList();
        for(int i = 0; i < storages.size(); i++) {
            NBTTagCompound tag = NBTUtil.createPosTag(storages.get(i));
            list.appendTag(tag);
        }

        compound.setTag("storages", list);

        return compound;
    }

    @Override
    public void readFromNBT(NBTTagCompound compound) {
        NBTTagList list = compound.getTagList("storages", Constants.NBT.TAG_COMPOUND);
        storages.clear();
        for (int i = 0; i < list.tagCount(); i++) {
            storages.add(NBTUtil.getPosFromTag(list.getCompoundTagAt(i)));
        }
        super.readFromNBT(compound);
    }
}
Модель я думаю Вы сможете сделать.
Основная модель - это просто кубик,
а сторона это продолжение кубика вверх.
{
"forge_marker": 1,
"defaults": {
"model": "redstonethings:wire",
"transform": "forge:default-block"
},
"variants": {
"down": {
"true": {
"submodel": "redstonethings:wire_side", "x": 180
},
"false": {}
},
"up": {
"true": {
"submodel": "redstonethings:wire_side"
},
"false": {}
},
"north": {
"true": {
"submodel": "redstonethings:wire_side", "x":90
},
"false": {}
},
"south": {
"true": {
"submodel": "redstonethings:wire_side", "x": 270
},
"false": {}
},
"west": {
"true": {
"submodel": "redstonethings:wire_side", "x": 270, "y":90
},
"false": {}
},
"east": {
"true": {
"submodel": "redstonethings:wire_side", "x": 90, "y":90
},
"false": {}
},
"inventory": [{}]
}
}
P.s. Частично использовал исходники мода @iconst_1
Если какие-то ошибки, то укажите.
Хорошо бы конечно переписать этот туториал человеку, который реально знает, что делает т.к. туториалов на эту тему практически нет, только вопросы на форуме forge.(но мечты мечтами) Многие, я думаю, хотят реализовать в своих модах провода(или сеть энергии). Или поправить мою кривую реализацию.
P.P.s пилю другой вариант реализации, должно быть получше.
Автор
GreedyCat
Просмотры
1,222
Первый выпуск
Обновление
Оценка
4.00 звёзд 1 оценок

Другие ресурсы пользователя GreedyCat

Последние рецензии

Идея в принципе неплохая, но при каждом добавлении и удалении блока происходит пересбор сети с нуля. Это неэффективно, лучше буферизировать сеть и при удалении и добавлении блоков обновлять имеющуюся сеть, а не строить ее заново.
К тому же сеть для каждого добавления и удаления пересобирается два раза из-за того, что при изменении соседей опять же вызывается buildNetwork.
Также нет логики передачи энергии по построенной сети, а ведь вроде так была поставлена задача
Сверху