package miscperipherals.tile; import java.util.Stack; import java.util.concurrent.Callable; import java.util.concurrent.Future; import miscperipherals.core.LuaManager; import miscperipherals.core.MiscPeripherals; import miscperipherals.core.TickHandler; import miscperipherals.util.Util; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.nbt.NBTTagString; import net.minecraft.server.MinecraftServer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ChunkCoordinates; import net.minecraft.util.Facing; import net.minecraft.world.World; import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.network.PacketDispatcher; import dan200.computer.api.IComputerAccess; import dan200.computer.api.IPeripheral; import dan200.turtle.api.ITurtleAccess; public class TileTeleporter extends Tile implements IPeripheral { public final Stack<LinkData> links = new Stack<LinkData>(); public final int maxLinks; private String error; public TileTeleporter() { this.maxLinks = 1; } public TileTeleporter(int maxLinks) { this.maxLinks = maxLinks; } @Override public String getType() { return "teleporter"; } @Override public String[] getMethodNames() { return new String[] {"teleport","tp","getError"}; } @Override public Object[] callMethod(IComputerAccess computer, int method, Object[] arguments) throws Exception { switch (method) { case 0: case 1: { if (arguments.length > 0 && !(arguments[0] instanceof Double)) throw new Exception("bad argument #1 (expected number)"); final int index = arguments.length > 0 ? (int)Math.floor((Double)arguments[0]) - 1 : 0; if (index < 0 || index >= maxLinks) throw new Exception("bad link "+(index+1)+" (expected 1-"+maxLinks+")"); Future<Boolean> callback = TickHandler.addTickCallback(worldObj, new Callable<Boolean>() { @Override public Boolean call() { if (index >= links.size()) { error = "No such link"; return false; } LinkData link = links.get(index); if (link == null) { error = "No such link"; return false; } TileEntity te = worldObj.getBlockTileEntity(xCoord + Facing.offsetsXForSide[getFacing()], yCoord + Facing.offsetsYForSide[getFacing()], zCoord + Facing.offsetsZForSide[getFacing()]); if (!(te instanceof ITurtleAccess)) { error = "No turtle in front"; return false; } World destWorld = MinecraftServer.getServer().worldServerForDimension(link.linkDim); if (destWorld == null) { error = "Destination world missing"; return false; } TileEntity dest = destWorld.getBlockTileEntity(link.link.posX, link.link.posY, link.link.posZ); if (!(dest instanceof TileTeleporter)) { error = "Destination is not a teleporter"; return false; } TileTeleporter teleporter = (TileTeleporter)dest; int linkToX = link.link.posX + Facing.offsetsXForSide[teleporter.getFacing()]; int linkToY = link.link.posY + Facing.offsetsYForSide[teleporter.getFacing()]; int linkToZ = link.link.posZ + Facing.offsetsZForSide[teleporter.getFacing()]; if (!Util.isPassthroughBlock(destWorld, linkToX, linkToY, linkToZ)) { error = "Destination obstructed"; return false; } int xdif = Math.abs(xCoord - link.link.posX); int ydif = Math.abs(yCoord - link.link.posY); int zdif = Math.abs(zCoord - link.link.posZ); ITurtleAccess turtle = (ITurtleAccess)te; if (!turtle.consumeFuel(Math.abs((int)Math.ceil((xdif + ydif + zdif) * (Math.abs(worldObj.provider.dimensionId - destWorld.provider.dimensionId) + 1) * MiscPeripherals.instance.teleporterPenalty)))) { error = "Not enough fuel"; return false; } boolean result = Util.teleportTurtleTo(turtle, destWorld, linkToX, linkToY, linkToZ); if (result) { int flag = worldObj.provider.dimensionId != destWorld.provider.dimensionId ? 1 : 0; ByteArrayDataOutput os = ByteStreams.newDataOutput(); os.writeInt(xCoord); os.writeInt(yCoord); os.writeInt(zCoord); os.writeByte(flag); PacketDispatcher.sendPacketToAllAround(xCoord + 0.5D, yCoord + 0.5D, zCoord + 0.5D, 64.0D, worldObj.provider.dimensionId, PacketDispatcher.getTinyPacket(MiscPeripherals.instance, (short)4, os.toByteArray())); onTeleport((byte)flag); os = ByteStreams.newDataOutput(); os.writeInt(teleporter.xCoord); os.writeInt(teleporter.yCoord); os.writeInt(teleporter.zCoord); os.writeByte(flag); PacketDispatcher.sendPacketToAllAround(teleporter.xCoord + 0.5D, teleporter.yCoord + 0.5D, teleporter.zCoord + 0.5D, 64.0D, destWorld.provider.dimensionId, PacketDispatcher.getTinyPacket(MiscPeripherals.instance, (short)4, os.toByteArray())); teleporter.onTeleport((byte)flag); } return result; } }); return new Object[] {callback.get()}; } case 2: { return new Object[] {error}; } } return new Object[0]; } @Override public boolean canAttachToSide(int side) { return true; } @Override public void attach(IComputerAccess computer) { LuaManager.mount(computer); } @Override public void detach(IComputerAccess computer) { } @Override public void readFromNBT(NBTTagCompound compound) { super.readFromNBT(compound); NBTTagList links = compound.getTagList("links"); for (int i = 0; i < links.tagCount(); i++) { NBTTagCompound link = (NBTTagCompound)links.tagAt(i); if (link.hasKey("linkX") && link.hasKey("linkY") && link.hasKey("linkZ") && link.hasKey("linkDim")) { this.links.add(new LinkData(compound.getInteger("linkDim"), new ChunkCoordinates(compound.getInteger("linkX"), compound.getInteger("linkY"), compound.getInteger("linkZ")))); } } } @Override public void writeToNBT(NBTTagCompound compound) { super.writeToNBT(compound); NBTTagList list = new NBTTagList(); for (int i = 0; i < links.size(); i++) { LinkData link = links.get(i); if (link != null) { NBTTagCompound lcompound = new NBTTagCompound(); lcompound.setInteger("linkX", link.link.posX); lcompound.setInteger("linkY", link.link.posY); lcompound.setInteger("linkZ", link.link.posZ); lcompound.setInteger("linkDim", link.linkDim); list.appendTag(lcompound); } } compound.setTag("links", list); } @Override public boolean onBlockActivated(EntityPlayer player, int side, float hitX, float hitY, float hitZ) { ItemStack held = player.getCurrentEquippedItem(); if (held != null && held.itemID == Item.redstoneRepeater.itemID) { if (held.stackTagCompound != null) { if (held.stackTagCompound.hasKey("miscperipheralsLinkX") && held.stackTagCompound.hasKey("miscperipheralsLinkY") && held.stackTagCompound.hasKey("miscperipheralsLinkZ") && held.stackTagCompound.hasKey("miscperipheralsLinkDim")) { ChunkCoordinates link = new ChunkCoordinates(held.stackTagCompound.getInteger("miscperipheralsLinkX"), held.stackTagCompound.getInteger("miscperipheralsLinkY"), held.stackTagCompound.getInteger("miscperipheralsLinkZ")); int linkDim = held.stackTagCompound.getInteger("miscperipheralsLinkDim"); World srcWorld = MinecraftServer.getServer().worldServerForDimension(linkDim); if (srcWorld == null) { player.sendChatToPlayer("Link failed: World is missing"); } else { TileEntity te = srcWorld.getBlockTileEntity(link.posX, link.posY, link.posZ); if (!(te instanceof TileTeleporter)) { player.sendChatToPlayer("Link failed: Teleporter no longer exists"); } else { TileTeleporter src = (TileTeleporter)te; if (link.posX == xCoord && link.posY == yCoord && link.posZ == zCoord) { player.sendChatToPlayer("Link canceled"); } else { boolean unlinked = false; for (int i = 0; i < src.links.size(); i++) { LinkData rlink = src.links.get(i); System.out.println("comparing: "+rlink.link.posX+" "+xCoord+" "+rlink.link.posY+" "+yCoord+" "+rlink.link.posZ+" "+zCoord); if (rlink.link.posX == xCoord && rlink.link.posY == yCoord && rlink.link.posZ == zCoord && rlink.linkDim == worldObj.provider.dimensionId) { player.sendChatToPlayer("Unlinked teleporter at "+rlink.linkDim+":("+rlink.link.posX+","+rlink.link.posY+","+rlink.link.posZ+") (link "+(i+1)+") from this teleporter"); src.links.remove(i); unlinked = true; break; } } if (!unlinked) { src.addLink(worldObj.provider.dimensionId, new ChunkCoordinates(xCoord, yCoord, zCoord)); player.sendChatToPlayer("Linked teleporter at "+linkDim+":("+link.posX+","+link.posY+","+link.posZ+") (link "+src.links.size()+") to this teleporter"); } } } } held.stackTagCompound.removeTag("miscperipheralsLinkX"); held.stackTagCompound.removeTag("miscperipheralsLinkY"); held.stackTagCompound.removeTag("miscperipheralsLinkZ"); held.stackTagCompound.removeTag("miscperipheralsLinkDim"); if (held.stackTagCompound.hasKey("display")) { NBTTagCompound display = held.stackTagCompound.getCompoundTag("display"); display.removeTag("Lore"); if (display.getTags().isEmpty()) { held.stackTagCompound.removeTag("display"); } else { held.stackTagCompound.setTag("display", display); } } return true; } } if (held.stackTagCompound == null) held.stackTagCompound = new NBTTagCompound(); held.stackTagCompound.setInteger("miscperipheralsLinkX", xCoord); held.stackTagCompound.setInteger("miscperipheralsLinkY", yCoord); held.stackTagCompound.setInteger("miscperipheralsLinkZ", zCoord); held.stackTagCompound.setInteger("miscperipheralsLinkDim", worldObj.provider.dimensionId); NBTTagCompound display = new NBTTagCompound(); NBTTagList lore = new NBTTagList(); lore.appendTag(new NBTTagString("", "Turtle Teleporter Link")); lore.appendTag(new NBTTagString("", worldObj.provider.dimensionId+":("+xCoord+","+yCoord+","+zCoord+")")); display.setTag("Lore", lore); held.stackTagCompound.setTag("display", display); player.sendChatToPlayer("Link started"); return true; } return false; } public void onTeleport(byte flag) { if (!MiscPeripherals.proxy.isServer()) { int facing = getFacing(); for (int j = 0; j < 32; j++) { worldObj.spawnParticle("portal", xCoord + 0.5D + 1.0D * Facing.offsetsXForSide[facing], yCoord + 1.0D * Facing.offsetsYForSide[facing] + worldObj.rand.nextDouble() * 2.0D, zCoord + 0.5D + 1.0D * Facing.offsetsZForSide[getFacing()], worldObj.rand.nextGaussian(), 0.0D, worldObj.rand.nextGaussian()); } } else { String sound = "mob.endermen.portal"; if (Loader.isModLoaded("Mystcraft")) sound = flag == 1 ? "myst.sound.link-portal" : "myst.sound.link-intra"; worldObj.playSoundEffect(xCoord + 0.5D, yCoord + 0.5D, zCoord + 0.5D, sound, 1.0F, 1.0F); } } public int addLink(int linkDim, ChunkCoordinates link) { links.add(new LinkData(linkDim, link)); while (links.size() > maxLinks) links.pop(); return links.size(); } protected static class LinkData { public final int linkDim; public final ChunkCoordinates link; public LinkData(int linkDim, ChunkCoordinates link) { this.linkDim = linkDim; this.link = link; } } }