/* * CraftBook Copyright (C) 2010-2017 sk89q <http://www.sk89q.com> * CraftBook Copyright (C) 2011-2017 me4502 <http://www.me4502.com> * CraftBook Copyright (C) Contributors * * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public * License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program. If not, * see <http://www.gnu.org/licenses/>. */ package com.sk89q.craftbook.sponge.mechanics.area; import com.google.common.collect.Lists; import com.google.inject.Inject; import com.me4502.modularframework.module.Module; import com.me4502.modularframework.module.guice.ModuleConfiguration; import com.sk89q.craftbook.core.util.ConfigValue; import com.sk89q.craftbook.core.util.CraftBookException; import com.sk89q.craftbook.core.util.PermissionNode; import com.sk89q.craftbook.core.util.documentation.DocumentationProvider; import com.sk89q.craftbook.sponge.CraftBookPlugin; import com.sk89q.craftbook.sponge.mechanics.blockbags.BlockBag; import com.sk89q.craftbook.sponge.mechanics.blockbags.EmbeddedBlockBag; import com.sk89q.craftbook.sponge.mechanics.blockbags.MultiBlockBag; import com.sk89q.craftbook.sponge.util.data.mutable.EmbeddedBlockBagData; import com.sk89q.craftbook.sponge.util.BlockFilter; import com.sk89q.craftbook.sponge.util.BlockUtil; import com.sk89q.craftbook.sponge.util.SignUtil; import ninja.leaping.configurate.ConfigurationNode; import org.spongepowered.api.block.BlockState; import org.spongepowered.api.block.BlockTypes; import org.spongepowered.api.block.tileentity.Sign; import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.data.key.Keys; import org.spongepowered.api.entity.EntityTypes; import org.spongepowered.api.entity.Item; import org.spongepowered.api.entity.living.Humanoid; import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.event.cause.Cause; import org.spongepowered.api.event.cause.NamedCause; import org.spongepowered.api.item.inventory.ItemStack; import org.spongepowered.api.text.Text; import org.spongepowered.api.util.Direction; import org.spongepowered.api.world.Location; import org.spongepowered.api.world.World; import java.util.List; @Module(id = "door", name = "Door", onEnable="onInitialize", onDisable="onDisable") public class Door extends SimpleArea implements DocumentationProvider { @Inject @ModuleConfiguration public ConfigurationNode config; private ConfigValue<Integer> maximumLength = new ConfigValue<>("maximum-length", "The maximum length the door can be.", 16); private ConfigValue<Integer> maximumWidth = new ConfigValue<>("maximum-width", "The maximum width each side of the door can be. The overall max width is this*2 + 1.", 5); @Override public void onInitialize() throws CraftBookException { super.loadCommonConfig(config); super.registerCommonPermissions(); maximumLength.load(config); maximumWidth.load(config); } @Override public boolean triggerMechanic(Location<World> block, Sign sign, Humanoid human, Boolean forceState) { if (!"[Door]".equals(SignUtil.getTextRaw(sign, 1))) { Direction back = "[Door Up]".equals(SignUtil.getTextRaw(sign, 1)) ? Direction.UP : Direction.DOWN; Location<World> baseBlock = block.getRelative(back); if(!BlockUtil.doesStatePassFilters(allowedBlocks.getValue(), baseBlock.getBlock())) { if (human instanceof CommandSource) ((CommandSource) human).sendMessage(Text.builder("Can't use this material for a door!").build()); return true; } Location<World> otherSide = BlockUtil.getNextMatchingSign(block, back, maximumLength.getValue(), this::isMechanicSign); if (otherSide == null) { if (human instanceof CommandSource) ((CommandSource) human).sendMessage(missingOtherEnd); return true; } Location<World> otherBase = otherSide.getRelative(back.getOpposite()); if(!baseBlock.getBlock().equals(otherBase.getBlock())) { if (human instanceof CommandSource) ((CommandSource) human).sendMessage(Text.builder("Both ends must be the same material!").build()); return true; } int leftBlocks = 0, rightBlocks = 0; //Default to 0. Single width bridge is the default. Location<World> left = baseBlock.getRelative(SignUtil.getLeft(block)); Location<World> right = baseBlock.getRelative(SignUtil.getRight(block)); //Calculate left distance Location<World> otherLeft = otherBase.getRelative(SignUtil.getLeft(block)); while(true) { if(leftBlocks >= maximumWidth.getValue()) break; if(left.getBlock().equals(baseBlock.getBlock()) && otherLeft.getBlock().equals(baseBlock.getBlock())) { leftBlocks ++; left = left.getRelative(SignUtil.getLeft(block)); otherLeft = otherLeft.getRelative(SignUtil.getLeft(block)); } else { break; } } //Calculate right distance Location<World> otherRight = otherBase.getRelative(SignUtil.getRight(block)); while(true) { if(rightBlocks >= maximumWidth.getValue()) break; if(right.getBlock().equals(baseBlock.getBlock()) && otherRight.getBlock().equals(baseBlock.getBlock())) { rightBlocks ++; right = right.getRelative(SignUtil.getRight(block)); otherRight = otherRight.getRelative(SignUtil.getRight(block)); } else { break; } } baseBlock = baseBlock.getRelative(back); BlockState type = block.getRelative(back).getBlock(); if (baseBlock.getBlock().equals(type) || (forceState != null && !forceState)) type = BlockTypes.AIR.getDefaultState(); ItemStack blockBagItem = ItemStack.builder().fromBlockState(otherBase.getBlock()).quantity(1 + leftBlocks + rightBlocks).build(); BlockBag blockBag = getBlockBag(sign.getLocation()); while (baseBlock.getBlockY() != otherSide.getBlockY() + (back == Direction.UP ? -1 : 1)) { if (type.getType() == BlockTypes.AIR || blockBag.has(Lists.newArrayList(blockBagItem.copy()))) { if (type.getType() == BlockTypes.AIR && baseBlock.getBlock().equals(otherBase.getBlock())) { for (ItemStack leftover : blockBag.add(Lists.newArrayList(blockBagItem.copy()))) { Item item = (Item) block.getExtent().createEntity(EntityTypes.ITEM, sign.getLocation().getPosition()); item.offer(Keys.REPRESENTED_ITEM, leftover.createSnapshot()); block.getExtent().spawnEntity(item, CraftBookPlugin.spongeInst().getCause().build()); } } else if (type.getType() != BlockTypes.AIR && !baseBlock.getBlock().equals(otherBase.getBlock())) { if (!blockBag.remove(Lists.newArrayList(blockBagItem.copy())).isEmpty()) { continue; } } baseBlock.setBlock(type, Cause.of(NamedCause.source(CraftBookPlugin.spongeInst().getContainer()))); left = baseBlock.getRelative(SignUtil.getLeft(block)); for (int i = 0; i < leftBlocks; i++) { if (type.getType() == BlockTypes.AIR && left.getBlock().equals(otherBase.getBlock())) { for (ItemStack leftover : blockBag.add(Lists.newArrayList(blockBagItem.copy()))) { Item item = (Item) block.getExtent().createEntity(EntityTypes.ITEM, sign.getLocation().getPosition()); item.offer(Keys.REPRESENTED_ITEM, leftover.createSnapshot()); block.getExtent().spawnEntity(item, CraftBookPlugin.spongeInst().getCause().build()); } } else if (type.getType() != BlockTypes.AIR && !left.getBlock().equals(otherBase.getBlock())) { if (!blockBag.remove(Lists.newArrayList(blockBagItem.copy())).isEmpty()) { continue; } } left.setBlock(type, Cause.of(NamedCause.source(CraftBookPlugin.spongeInst().getContainer()))); left = left.getRelative(SignUtil.getLeft(block)); } right = baseBlock.getRelative(SignUtil.getRight(block)); for (int i = 0; i < rightBlocks; i++) { if (type.getType() == BlockTypes.AIR && right.getBlock().equals(otherBase.getBlock())) { for (ItemStack leftover : blockBag.add(Lists.newArrayList(blockBagItem.copy()))) { Item item = (Item) block.getExtent().createEntity(EntityTypes.ITEM, sign.getLocation().getPosition()); item.offer(Keys.REPRESENTED_ITEM, leftover.createSnapshot()); block.getExtent().spawnEntity(item, CraftBookPlugin.spongeInst().getCause().build()); } } else if (type.getType() != BlockTypes.AIR && !right.getBlock().equals(otherBase.getBlock())) { if (!blockBag.remove(Lists.newArrayList(blockBagItem.copy())).isEmpty()) { continue; } } right.setBlock(type, Cause.of(NamedCause.source(CraftBookPlugin.spongeInst().getContainer()))); right = right.getRelative(SignUtil.getRight(block)); } baseBlock = baseBlock.getRelative(back); } else { if (human instanceof Player) { ((Player) human).sendMessage(Text.of("Out of blocks!")); } break; } } if (blockBag instanceof EmbeddedBlockBag) { sign.getLocation().offer(new EmbeddedBlockBagData((EmbeddedBlockBag) blockBag)); } } else { if (human instanceof CommandSource) ((CommandSource) human).sendMessage(Text.builder("Door not activatable from here!").build()); return false; } return true; } @Override public BlockBag getBlockBag(Location<World> location) { BlockBag mainBlockBag = super.getBlockBag(location); if (SignUtil.isSign(location)) { Direction back = "[Door Up]".equals(SignUtil.getTextRaw((Sign) location.getTileEntity().get(), 1)) ? Direction.UP : Direction.DOWN; Location<World> next = BlockUtil.getNextMatchingSign(location, back, maximumLength.getValue() + 2, this::isMechanicSign); if (next != null) { BlockBag nextBlockBag = super.getBlockBag(next); if (nextBlockBag != null) { return new MultiBlockBag(mainBlockBag, nextBlockBag); } } } return mainBlockBag; } @Override public String[] getValidSigns() { return new String[]{"[Door Up]", "[Door Down]", "[Door]"}; } @Override public List<BlockFilter> getDefaultBlocks() { List<BlockFilter> states = Lists.newArrayList(); states.add(new BlockFilter("PLANKS")); states.add(new BlockFilter("COBBLESTONE")); return states; } @Override public String getPath() { return "mechanics/door"; } @Override public ConfigValue<?>[] getConfigurationNodes() { return new ConfigValue<?>[]{ allowedBlocks, allowRedstone, maximumLength, maximumWidth }; } @Override public PermissionNode[] getPermissionNodes() { return new PermissionNode[]{ createPermissions, usePermissions }; } }