package mcjty.rftools.blocks.blockprotector; import cpw.mods.fml.common.Optional; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; import li.cil.oc.api.machine.Arguments; import li.cil.oc.api.machine.Callback; import li.cil.oc.api.machine.Context; import li.cil.oc.api.network.SimpleComponent; import mcjty.lib.entity.GenericEnergyReceiverTileEntity; import mcjty.lib.entity.SyncedValueSet; import mcjty.lib.network.Argument; import mcjty.lib.varia.BlockTools; import mcjty.lib.varia.Coordinate; import mcjty.lib.varia.GlobalCoordinate; import mcjty.lib.varia.Logging; import mcjty.rftools.blocks.RedstoneMode; import mcjty.rftools.items.smartwrench.SmartWrenchSelector; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumChatFormatting; import net.minecraftforge.common.util.ForgeDirection; import java.util.Map; import java.util.Set; @Optional.InterfaceList({ @Optional.Interface(iface = "li.cil.oc.api.network.SimpleComponent", modid = "OpenComputers"), @Optional.Interface(iface = "dan200.computercraft.api.peripheral.IPeripheral", modid = "ComputerCraft")}) public class BlockProtectorTileEntity extends GenericEnergyReceiverTileEntity implements SmartWrenchSelector, SimpleComponent, IPeripheral { public static final String CMD_RSMODE = "rsMode"; public static final String COMPONENT_NAME = "block_protector"; private RedstoneMode redstoneMode = RedstoneMode.REDSTONE_IGNORED; private int powered = 0; private int id = -1; // Relative coordinates (relative to this tile entity) private SyncedValueSet<Coordinate> protectedBlocks = new SyncedValueSet<Coordinate>() { @Override public Coordinate readElementFromNBT(NBTTagCompound tagCompound) { return Coordinate.readFromNBT(tagCompound, "c"); } @Override public NBTTagCompound writeElementToNBT(Coordinate element) { return Coordinate.writeToNBT(element); } }; @Override @Optional.Method(modid = "ComputerCraft") public String getType() { return COMPONENT_NAME; } @Override @Optional.Method(modid = "ComputerCraft") public String[] getMethodNames() { return new String[] { "getRedstoneMode", "setRedstoneMode" }; } @Override @Optional.Method(modid = "ComputerCraft") public Object[] callMethod(IComputerAccess computer, ILuaContext context, int method, Object[] arguments) throws LuaException, InterruptedException { switch (method) { case 0: return new Object[] { getRedstoneMode().getDescription() }; case 1: return setRedstoneMode((String) arguments[0]); } return new Object[0]; } @Override @Optional.Method(modid = "ComputerCraft") public void attach(IComputerAccess computer) { } @Override @Optional.Method(modid = "ComputerCraft") public void detach(IComputerAccess computer) { } @Override @Optional.Method(modid = "ComputerCraft") public boolean equals(IPeripheral other) { return false; } @Override @Optional.Method(modid = "OpenComputers") public String getComponentName() { return COMPONENT_NAME; } @Callback(doc = "Get the current redstone mode. Values are 'Ignored', 'Off', or 'On'", getter = true) @Optional.Method(modid = "OpenComputers") public Object[] getRedstoneMode(Context context, Arguments args) throws Exception { return new Object[] { getRedstoneMode().getDescription() }; } @Callback(doc = "Set the current redstone mode. Values are 'Ignored', 'Off', or 'On'", setter = true) @Optional.Method(modid = "OpenComputers") public Object[] setRedstoneMode(Context context, Arguments args) throws Exception { String mode = args.checkString(0); return setRedstoneMode(mode); } public BlockProtectorTileEntity() { super(BlockProtectorConfiguration.MAXENERGY, BlockProtectorConfiguration.RECEIVEPERTICK); registerSyncedObject(protectedBlocks); } @Override protected void checkStateServer() { if (protectedBlocks.isEmpty()) { setState(0); return; } if (isDisabled()) { setState(0); return; } else { setState(1); } consumeEnergy(protectedBlocks.size() * BlockProtectorConfiguration.rfPerProtectedBlock); } private void setState(int state) { int metadata = worldObj.getBlockMetadata(xCoord, yCoord, zCoord); int newmeta = BlockTools.setState(metadata, state); if (newmeta != metadata) { worldObj.setBlockMetadataWithNotify(xCoord, yCoord, zCoord, newmeta, 2); } } private boolean isDisabled() { if (redstoneMode != RedstoneMode.REDSTONE_IGNORED) { boolean rs = powered > 0; if (redstoneMode == RedstoneMode.REDSTONE_OFFREQUIRED) { if (rs) { return true; } } else if (redstoneMode == RedstoneMode.REDSTONE_ONREQUIRED) { if (!rs) { return true; } } } return false; } @Override public void setPowered(int powered) { if (this.powered != powered) { this.powered = powered; markDirty(); } } private Object[] setRedstoneMode(String mode) { RedstoneMode redstoneMode = RedstoneMode.getMode(mode); if (redstoneMode == null) { throw new IllegalArgumentException("Not a valid mode"); } setRedstoneMode(redstoneMode); return null; } public RedstoneMode getRedstoneMode() { return redstoneMode; } public void setRedstoneMode(RedstoneMode redstoneMode) { this.redstoneMode = redstoneMode; worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); markDirty(); } public boolean attemptHarvestProtection() { if (isDisabled()) return false; int rf = getEnergyStored(ForgeDirection.DOWN); if (BlockProtectorConfiguration.rfForHarvestAttempt > rf) { return false; } consumeEnergy(BlockProtectorConfiguration.rfForHarvestAttempt); return true; } // Distance is relative with 0 being closes to the explosion and 1 being furthest away. public int attemptExplosionProtection(float distance, float radius) { if (isDisabled()) return -1; int rf = getEnergyStored(ForgeDirection.DOWN); int rfneeded = (int) (BlockProtectorConfiguration.rfForExplosionProtection * (1.0 - distance) * radius / 8.0f) + 1; rfneeded = (int) (rfneeded * (2.0f - getInfusedFactor()) / 2.0f); if (rfneeded > rf) { return -1; } if (rfneeded <= 0) { rfneeded = 1; } consumeEnergy(rfneeded); return rfneeded; } public Set<Coordinate> getProtectedBlocks() { return protectedBlocks; } public Coordinate absoluteToRelative(Coordinate c) { return absoluteToRelative(c.getX(), c.getY(), c.getZ()); } public Coordinate absoluteToRelative(int x, int y, int z) { return new Coordinate(x - xCoord, y - yCoord, z - zCoord); } // Test if this relative coordinate is protected. public boolean isProtected(Coordinate c) { return protectedBlocks.contains(c); } // Used by the explosion event handler. public void removeProtection(Coordinate relative) { protectedBlocks.remove(relative); markDirty(); notifyBlockUpdate(); } // Toggle a coordinate to be protected or not. The coordinate given here is absolute. public void toggleCoordinate(GlobalCoordinate c) { if (c.getDimension() != worldObj.provider.dimensionId) { // Wrong dimension. Don't do anything. return; } Coordinate relative = absoluteToRelative(c.getCoordinate()); if (protectedBlocks.contains(relative)) { protectedBlocks.remove(relative); } else { protectedBlocks.add(relative); } markDirty(); notifyBlockUpdate(); } @Override public void selectBlock(EntityPlayer player, int x, int y, int z) { // This is always called server side. if (Math.abs(x-xCoord) > BlockProtectorConfiguration.maxProtectDistance || Math.abs(y-yCoord) > BlockProtectorConfiguration.maxProtectDistance || Math.abs(z-zCoord) > BlockProtectorConfiguration.maxProtectDistance) { Logging.message(player, EnumChatFormatting.RED + "Block out of range of the block protector!"); return; } GlobalCoordinate gc = new GlobalCoordinate(new Coordinate(x, y, z), worldObj.provider.dimensionId); toggleCoordinate(gc); } public int getOrCalculateID() { if (id == -1) { BlockProtectors protectors = BlockProtectors.getProtectors(worldObj); GlobalCoordinate gc = new GlobalCoordinate(new Coordinate(xCoord, yCoord, zCoord), worldObj.provider.dimensionId); id = protectors.getNewId(gc); protectors.save(worldObj); setId(id); } return id; } public int getId() { return id; } public void setId(int id) { this.id = id; worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); markDirty(); } /** * This method is called after putting down a protector that was earlier wrenched. We need to fix the data in * the destination. */ public void updateDestination() { BlockProtectors protectors = BlockProtectors.getProtectors(worldObj); GlobalCoordinate gc = new GlobalCoordinate(new Coordinate(xCoord, yCoord, zCoord), worldObj.provider.dimensionId); if (id == -1) { id = protectors.getNewId(gc); markDirty(); } else { protectors.assignId(gc, id); } protectors.save(worldObj); worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); } @Override public void readFromNBT(NBTTagCompound tagCompound) { super.readFromNBT(tagCompound); protectedBlocks.readFromNBT(tagCompound, "coordinates"); powered = tagCompound.getByte("powered"); } @Override public void readRestorableFromNBT(NBTTagCompound tagCompound) { super.readRestorableFromNBT(tagCompound); if (tagCompound.hasKey("protectorId")) { id = tagCompound.getInteger("protectorId"); } else { id = -1; } int m = tagCompound.getByte("rsMode"); redstoneMode = RedstoneMode.values()[m]; } @Override public void writeToNBT(NBTTagCompound tagCompound) { super.writeToNBT(tagCompound); protectedBlocks.writeToNBT(tagCompound, "coordinates"); tagCompound.setByte("powered", (byte) powered); } @Override public void writeRestorableToNBT(NBTTagCompound tagCompound) { super.writeRestorableToNBT(tagCompound); tagCompound.setInteger("protectorId", id); tagCompound.setByte("rsMode", (byte) redstoneMode.ordinal()); } @Override public boolean execute(EntityPlayerMP playerMP, String command, Map<String, Argument> args) { boolean rc = super.execute(playerMP, command, args); if (rc) { return true; } if (CMD_RSMODE.equals(command)) { String m = args.get("rs").getString(); setRedstoneMode(RedstoneMode.getMode(m)); return true; } return false; } }