package com.rwtema.funkylocomotion.blocks; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import com.mojang.authlib.GameProfile; import com.rwtema.funkylocomotion.FunkyLocomotion; import com.rwtema.funkylocomotion.api.FunkyCapabilities; import com.rwtema.funkylocomotion.api.IAdvStickyBlock; import com.rwtema.funkylocomotion.api.IStickyBlock; import com.rwtema.funkylocomotion.helper.BlockHelper; import com.rwtema.funkylocomotion.movers.IMover; import com.rwtema.funkylocomotion.movers.MoveManager; import com.rwtema.funkylocomotion.movers.MoverEventHandler; import com.rwtema.funkylocomotion.particles.ObstructionHelper; import com.rwtema.funkylocomotion.proxydelegates.ProxyRegistry; import net.minecraft.block.Block; import net.minecraft.block.BlockDirectional; import net.minecraft.block.state.IBlockState; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.ITickable; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.energy.CapabilityEnergy; public class TilePusher extends TilePowered implements IMover, ITickable { public static final int[] moveTime = new int[]{ 20, 10, 7, 5, 4, 3 }; public static final int COOLDOWN_TIMER = 2; public static int maxTiles = 1024; public static int powerPerTile = 250; public boolean powered; @Nullable protected GameProfile profile; int cooldown = -1; public TilePusher() { super(maxTiles * powerPerTile); } @Override public void readFromNBT(NBTTagCompound tag) { super.readFromNBT(tag); cooldown = tag.getInteger("cooldown"); powered = tag.getBoolean("Powered"); String name = tag.getString("Name"); UUID uuid; if (tag.hasKey("UUIDL")) { uuid = new UUID(tag.getLong("UUIDU"), tag.getLong("UUIDL")); profile = new GameProfile(uuid, name); } else { if (StringUtils.isBlank(name)) profile = null; else profile = new GameProfile(null, name); } } @Nonnull @Override public NBTTagCompound writeToNBT(NBTTagCompound tag) { super.writeToNBT(tag); tag.setInteger("cooldown", cooldown); tag.setBoolean("powered", powered); NBTTagCompound profileTag = new NBTTagCompound(); if (profile != null) { profileTag.setString("Name", profile.getName()); UUID id = profile.getId(); if (id != null) { profileTag.setLong("UUIDL", id.getLeastSignificantBits()); profileTag.setLong("UUIDU", id.getMostSignificantBits()); } tag.setTag("profile", profileTag); } return tag; } @Override public boolean shouldRefresh(World world, BlockPos pos, @Nonnull IBlockState oldState, @Nonnull IBlockState newSate) { return oldState.getBlock() != newSate.getBlock(); } public List<BlockPos> getBlocks(World world, BlockPos home, EnumFacing dir, boolean push) { if (push) { BlockPos advance = home.offset(dir); if (BlockHelper.canStick(world, advance, dir.getOpposite(), profile)) return getBlocks(world, home, advance, dir); } else { dir = dir.getOpposite(); BlockPos advance = home.offset(dir); if (!world.isAirBlock(advance)) return null; BlockPos advance2 = advance.offset(dir); if (BlockHelper.canStick(world, advance2, dir.getOpposite(), profile)) return getBlocks(world, home, advance2, dir.getOpposite()); } return null; } public List<BlockPos> getBlocks(World world, BlockPos home, BlockPos start, EnumFacing moveDir) { ArrayList<BlockPos> posList = new ArrayList<>(); HashSet<BlockPos> posSet = new HashSet<>(); getBlockPosIterate(world, home, start, posList, posSet); return checkPositions(world, moveDir, posList, posSet); } public List<BlockPos> checkPositions(World world, EnumFacing moveDir, ArrayList<BlockPos> posList, HashSet<BlockPos> posSet) { boolean fail = false; for (BlockPos pos : posList) { BlockPos adv = pos.offset(moveDir); if (!posSet.contains(adv) && !BlockHelper.canReplace(world, adv)) { if (!ObstructionHelper.sendObstructionPacket(world, pos, moveDir)) return null; fail = true; } } return fail ? null : posList; } private void getBlockPosIterate(World world, BlockPos home, BlockPos start, ArrayList<BlockPos> posList, HashSet<BlockPos> posSet) { LinkedList<BlockPos> toIterate = new LinkedList<>(); HashSet<BlockPos> toIterateSet = new HashSet<>(); toIterate.add(start); toIterateSet.add(start); while (!toIterate.isEmpty()) { BlockPos pos = toIterate.poll(); posList.add(pos); posSet.add(pos); IBlockState state = world.getBlockState(pos); Block b = state.getBlock(); TileEntity tile = world.getTileEntity(pos); IAdvStickyBlock advStickyBlock = ProxyRegistry.getInterface(b, IAdvStickyBlock.class, FunkyCapabilities.ADV_STICKY_BLOCK); if(tile != null && advStickyBlock == null) advStickyBlock = ProxyRegistry.getInterface(tile, IAdvStickyBlock.class, FunkyCapabilities.ADV_STICKY_BLOCK); if (advStickyBlock != null) { for (BlockPos blockPos : advStickyBlock.getBlocksToMove(world, pos)) { if (home.equals(blockPos)) continue; if (toIterateSet.contains(blockPos)) continue; if (!BlockHelper.isValid(world, blockPos) || !BlockHelper.canMoveBlock(world, blockPos, profile)) continue; BlockPos immutableBlockPos = blockPos.toImmutable(); toIterate.add(immutableBlockPos); toIterateSet.add(immutableBlockPos); } } else { IStickyBlock stickyBlock = ProxyRegistry.getInterface(b, IStickyBlock.class, FunkyCapabilities.STICKY_BLOCK); if(tile != null && stickyBlock == null) stickyBlock = ProxyRegistry.getInterface(tile, IStickyBlock.class, FunkyCapabilities.STICKY_BLOCK); if (stickyBlock != null) { for (EnumFacing side : EnumFacing.values()) { if (stickyBlock.isStickySide(world, pos, side)) { BlockPos newPos = pos.offset(side); if (home.equals(newPos)) continue; if (toIterateSet.contains(newPos)) continue; if (BlockHelper.canStick(world, newPos, side.getOpposite(), profile)) { toIterate.add(newPos); toIterateSet.add(newPos); } } } } } } } public void startMoving() { cooldown = -1; int meta = getBlockMetadata(); EnumFacing dir1 = EnumFacing.values()[meta % 6].getOpposite(); boolean push1 = meta < 6; EnumFacing d2 = push1 ? dir1 : dir1.getOpposite(); EnumFacing dir = d2.getOpposite(); boolean push = meta < 6; List<BlockPos> posList = getBlocks(getWorld(), pos, dir, push); if (posList != null) { final int energy = posList.size() * powerPerTile; if (this.energy.extractEnergy(energy, true) != energy) return; ArrayList<TileBooster> boosters = new ArrayList<>(6); for (EnumFacing d : EnumFacing.values()) { if (d != dir) { BlockPos p = pos.offset(d); IBlockState state = getWorld().getBlockState(p); if (state.getBlock() == FunkyLocomotion.booster) { if (state.getValue(BlockDirectional.FACING) != d.getOpposite()) continue; TileEntity tile = BlockHelper.getTile(getWorld(), p); if (tile instanceof TileBooster) { TileBooster booster = (TileBooster) tile; if (booster.energy.extractEnergy(energy, true) != energy) continue; boosters.add(booster); } } } } if (!boosters.isEmpty()) { for (TileBooster booster : boosters) { booster.energy.extractEnergy(energy, false); } } this.energy.extractEnergy(energy, false); MoveManager.startMoving(getWorld(), posList, getDirection(), moveTime[boosters.size()]); } } @Override public boolean stillExists() { return !tileEntityInvalid && getWorld() != null && getWorld().isBlockLoaded(pos) && getWorld().getTileEntity(pos) == this; } @Override public void onChunkUnload() { tileEntityInvalid = true; } public EnumFacing getDirection() { int meta = getBlockMetadata(); EnumFacing dir = EnumFacing.values()[meta % 6].getOpposite(); boolean push = meta < 6; return push ? dir.getOpposite() : dir; } @Override public boolean hasCapability(@Nonnull Capability<?> capability, @Nonnull EnumFacing facing) { return capability == CapabilityEnergy.ENERGY || super.hasCapability(capability, facing); } @Nonnull @Override public <T> T getCapability(@Nonnull Capability<T> capability, @Nonnull EnumFacing facing) { if (capability == CapabilityEnergy.ENERGY) { return CapabilityEnergy.ENERGY.cast(energy); } return super.getCapability(capability, facing); } @Override public void update() { if (! this.getWorld().isRemote) { if (cooldown > 0) { cooldown--; } if (cooldown == 0) { MoverEventHandler.registerMover(this); } } } public void startCooldown() { if (cooldown == -1) { cooldown = COOLDOWN_TIMER; } } }