package mcjty.rftools.blocks.dimlets;
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.container.InventoryHelper;
import mcjty.lib.entity.GenericEnergyReceiverTileEntity;
import mcjty.lib.network.Argument;
import mcjty.lib.network.PacketRequestIntegerFromServer;
import mcjty.lib.varia.BlockTools;
import mcjty.lib.varia.Logging;
import mcjty.rftools.blocks.RedstoneMode;
import mcjty.rftools.dimension.DimensionStorage;
import mcjty.rftools.dimension.RfToolsDimensionManager;
import mcjty.rftools.dimension.description.DimensionDescriptor;
import mcjty.rftools.network.RFToolsMessages;
import net.minecraft.block.Block;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.common.util.ForgeDirection;
import java.util.Map;
import java.util.Random;
@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 DimensionBuilderTileEntity extends GenericEnergyReceiverTileEntity implements ISidedInventory, SimpleComponent, IPeripheral {
public static final String CMD_GETBUILDING = "getBuilding";
public static final String CLIENTCMD_GETBUILDING = "getBuilding";
public static final String CMD_RSMODE = "rsMode";
public static final String COMPONENT_NAME = "dimension_builder";
// For usage in the gui
private static int buildPercentage = 0;
private int creative = -1; // -1 is unknown
private RedstoneMode redstoneMode = RedstoneMode.REDSTONE_IGNORED;
private int powered = 0;
public static int OK = 0;
public static int ERROR_NOOWNER = -1;
public static int ERROR_TOOMANYDIMENSIONS = -2;
private int errorMode = 0;
private InventoryHelper inventoryHelper = new InventoryHelper(this, DimensionBuilderContainer.factory, 1);
public DimensionBuilderTileEntity() {
super(DimletConfiguration.BUILDER_MAXENERGY, DimletConfiguration.BUILDER_RECEIVEPERTICK);
}
private boolean isCreative() {
if (creative == -1) {
Block block = worldObj.getBlock(xCoord, yCoord, zCoord);
if (DimletSetup.creativeDimensionBuilderBlock.equals(block)) {
creative = 1;
} else {
creative = 0;
}
}
return creative == 1;
}
@Override
protected void checkStateServer() {
NBTTagCompound tagCompound = hasTab();
if (tagCompound == null) {
setState(-1);
return;
}
if (redstoneMode != RedstoneMode.REDSTONE_IGNORED) {
boolean rs = powered > 0;
if (redstoneMode == RedstoneMode.REDSTONE_OFFREQUIRED) {
if (rs) {
setState(-1);
return;
}
} else if (redstoneMode == RedstoneMode.REDSTONE_ONREQUIRED) {
if (!rs) {
setState(-1);
return;
}
}
}
int ticksLeft = tagCompound.getInteger("ticksLeft");
if (ticksLeft > 0) {
ticksLeft = createDimensionTick(tagCompound, ticksLeft);
} else {
maintainDimensionTick(tagCompound);
}
setState(ticksLeft);
}
@Override
public void setPowered(int powered) {
if (this.powered != powered) {
this.powered = powered;
markDirty();
}
}
@Override
@Optional.Method(modid = "ComputerCraft")
public String getType() {
return COMPONENT_NAME;
}
@Override
@Optional.Method(modid = "ComputerCraft")
public String[] getMethodNames() {
return new String[] { "hasTab", "getBuildingPercentage", "getDimensionPower", "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[] { hasTab() != null };
case 1: return getBuildingPercentage();
case 2: return getDimensionPower();
case 3: return new Object[] { getRedstoneMode().getDescription() };
case 4: 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 = "Return true if this builder has a dimension tab in it", getter = true)
@Optional.Method(modid = "OpenComputers")
public Object[] hasTab(Context context, Arguments args) throws Exception {
return new Object[] { hasTab() != null };
}
@Callback(doc = "Return the current dimension building progress as a percentage", getter = true)
@Optional.Method(modid = "OpenComputers")
public Object[] getBuildingPercentage(Context context, Arguments args) throws Exception {
return getBuildingPercentage();
}
private Object[] getBuildingPercentage() {
NBTTagCompound tagCompound = hasTab();
if (tagCompound != null) {
int ticksLeft = tagCompound.getInteger("ticksLeft");
int tickCost = tagCompound.getInteger("tickCost");
int pct = (tickCost - ticksLeft) * 100 / tickCost;
return new Object[] { pct };
} else {
return new Object[] { 0 };
}
}
@Callback(doc = "Return how much power (RF) the dimension in the builder is currently having", getter = true)
@Optional.Method(modid = "OpenComputers")
public Object[] getDimensionPower(Context context, Arguments args) throws Exception {
return getDimensionPower();
}
private Object[] getDimensionPower() {
NBTTagCompound tagCompound = hasTab();
if (tagCompound != null) {
int id = tagCompound.getInteger("id");
int power = 0;
if (id != 0) {
DimensionStorage dimensionStorage = DimensionStorage.getDimensionStorage(worldObj);
power = dimensionStorage.getEnergyLevel(id);
}
return new Object[] { power };
} else {
return new Object[] { 0 };
}
}
@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);
}
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();
}
private NBTTagCompound hasTab() {
ItemStack itemStack = inventoryHelper.getStackInSlot(0);
if (itemStack == null || itemStack.stackSize == 0) {
return null;
}
NBTTagCompound tagCompound = itemStack.getTagCompound();
return tagCompound;
}
private static int counter = 20;
private void maintainDimensionTick(NBTTagCompound tagCompound) {
int id = tagCompound.getInteger("id");
if (id != 0) {
DimensionStorage dimensionStorage = DimensionStorage.getDimensionStorage(worldObj);
int rf;
if (isCreative()) {
rf = DimletConfiguration.BUILDER_MAXENERGY;
} else {
rf = getEnergyStored(ForgeDirection.DOWN);
}
int energy = dimensionStorage.getEnergyLevel(id);
int maxEnergy = DimletConfiguration.MAX_DIMENSION_POWER - energy; // Max energy the dimension can still get.
if (rf > maxEnergy) {
rf = maxEnergy;
}
counter--;
if (counter < 0) {
counter = 20;
if (Logging.debugMode) {
Logging.log("#################### id:" + id + ", rf:" + rf + ", energy:" + energy + ", max:" + maxEnergy);
}
}
if (!isCreative()) {
consumeEnergy(rf);
}
dimensionStorage.setEnergyLevel(id, energy + rf);
dimensionStorage.save(worldObj);
}
}
private static Random random = new Random();
private int createDimensionTick(NBTTagCompound tagCompound, int ticksLeft) {
if (DimletConfiguration.dimensionBuilderNeedsOwner) {
if (getOwnerUUID() == null) {
// No valid owner so we don't build the dimension.
errorMode = ERROR_NOOWNER;
return ticksLeft;
}
if (DimletConfiguration.maxDimensionsPerPlayer >= 0) {
int tickCost = tagCompound.getInteger("tickCost");
if (ticksLeft == tickCost || ticksLeft < 5) {
// Check if we are allow to make the dimension.
RfToolsDimensionManager manager = RfToolsDimensionManager.getDimensionManager(worldObj);
int cnt = manager.countOwnedDimensions(getOwnerUUID());
if (cnt >= DimletConfiguration.maxDimensionsPerPlayer) {
errorMode = ERROR_TOOMANYDIMENSIONS;
return ticksLeft;
}
}
}
}
errorMode = OK;
int createCost = tagCompound.getInteger("rfCreateCost");
createCost = (int) (createCost * (2.0f - getInfusedFactor()) / 2.0f);
if (isCreative() || (getEnergyStored(ForgeDirection.DOWN) >= createCost)) {
if (isCreative()) {
ticksLeft = 0;
} else {
consumeEnergy(createCost);
ticksLeft--;
if (random.nextFloat() < getInfusedFactor()) {
// Randomly reduce another tick if the device is infused.
ticksLeft--;
if (ticksLeft < 0) {
ticksLeft = 0;
}
}
}
tagCompound.setInteger("ticksLeft", ticksLeft);
if (ticksLeft <= 0) {
RfToolsDimensionManager manager = RfToolsDimensionManager.getDimensionManager(worldObj);
DimensionDescriptor descriptor = new DimensionDescriptor(tagCompound);
String name = tagCompound.getString("name");
int id = manager.createNewDimension(worldObj, descriptor, name, getOwnerName(), getOwnerUUID());
tagCompound.setInteger("id", id);
}
}
return ticksLeft;
}
private void setState(int ticksLeft) {
int state = 0;
if (ticksLeft == 0) {
state = 0;
} else if (ticksLeft == -1) {
state = 1;
} else if (((ticksLeft >> 2) & 1) == 0) {
state = 2;
} else {
state = 3;
}
int metadata = worldObj.getBlockMetadata(xCoord, yCoord, zCoord);
int newmeta = BlockTools.setState(metadata, state);
if (newmeta != metadata) {
worldObj.setBlockMetadataWithNotify(xCoord, yCoord, zCoord, newmeta, 2);
}
}
@Override
public int[] getAccessibleSlotsFromSide(int side) {
return DimensionBuilderContainer.factory.getAccessibleSlots();
}
@Override
public boolean canInsertItem(int index, ItemStack item, int side) {
return DimensionBuilderContainer.factory.isInputSlot(index);
}
@Override
public boolean canExtractItem(int index, ItemStack item, int side) {
return DimensionBuilderContainer.factory.isOutputSlot(index);
}
@Override
public int getSizeInventory() {
return inventoryHelper.getCount();
}
@Override
public ItemStack getStackInSlot(int index) {
return inventoryHelper.getStackInSlot(index);
}
@Override
public ItemStack decrStackSize(int index, int amount) {
return inventoryHelper.decrStackSize(index, amount);
}
@Override
public ItemStack getStackInSlotOnClosing(int index) {
return null;
}
@Override
public void setInventorySlotContents(int index, ItemStack stack) {
inventoryHelper.setInventorySlotContents(getInventoryStackLimit(), index, stack);
}
@Override
public String getInventoryName() {
return "Builder Inventory";
}
@Override
public boolean hasCustomInventoryName() {
return false;
}
@Override
public int getInventoryStackLimit() {
return 1;
}
@Override
public boolean isUseableByPlayer(EntityPlayer player) {
return canPlayerAccess(player);
}
@Override
public void openInventory() {
}
@Override
public void closeInventory() {
}
@Override
public boolean isItemValidForSlot(int index, ItemStack stack) {
return true;
}
// Request the building percentage from the server. This has to be called on the client side.
public void requestBuildingPercentage() {
RFToolsMessages.INSTANCE.sendToServer(new PacketRequestIntegerFromServer(xCoord, yCoord, zCoord,
CMD_GETBUILDING,
CLIENTCMD_GETBUILDING));
}
@Override
public Integer executeWithResultInteger(String command, Map<String, Argument> args) {
Integer rc = super.executeWithResultInteger(command, args);
if (rc != null) {
return rc;
}
if (CMD_GETBUILDING.equals(command)) {
ItemStack itemStack = inventoryHelper.getStackInSlot(0);
if (itemStack == null || itemStack.stackSize == 0) {
return 0;
} else {
NBTTagCompound tagCompound = itemStack.getTagCompound();
if (tagCompound == null) {
return 0;
}
if (errorMode != OK) {
return errorMode;
} else {
int ticksLeft = tagCompound.getInteger("ticksLeft");
int tickCost = tagCompound.getInteger("tickCost");
return (tickCost - ticksLeft) * 100 / tickCost;
}
}
}
return null;
}
@Override
public boolean execute(String command, Integer result) {
boolean rc = super.execute(command, result);
if (rc) {
return true;
}
if (CLIENTCMD_GETBUILDING.equals(command)) {
buildPercentage = result;
return true;
}
return false;
}
@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;
}
public static int getBuildPercentage() {
return buildPercentage;
}
@Override
public void readFromNBT(NBTTagCompound tagCompound) {
super.readFromNBT(tagCompound);
powered = tagCompound.getByte("powered");
}
@Override
public void readRestorableFromNBT(NBTTagCompound tagCompound) {
super.readRestorableFromNBT(tagCompound);
readBufferFromNBT(tagCompound);
int m = tagCompound.getByte("rsMode");
redstoneMode = RedstoneMode.values()[m];
}
private void readBufferFromNBT(NBTTagCompound tagCompound) {
NBTTagList bufferTagList = tagCompound.getTagList("Items", Constants.NBT.TAG_COMPOUND);
for (int i = 0 ; i < bufferTagList.tagCount() ; i++) {
NBTTagCompound nbtTagCompound = bufferTagList.getCompoundTagAt(i);
inventoryHelper.setStackInSlot(i, ItemStack.loadItemStackFromNBT(nbtTagCompound));
}
}
@Override
public void writeToNBT(NBTTagCompound tagCompound) {
super.writeToNBT(tagCompound);
tagCompound.setByte("powered", (byte) powered);
}
@Override
public void writeRestorableToNBT(NBTTagCompound tagCompound) {
super.writeRestorableToNBT(tagCompound);
writeBufferToNBT(tagCompound);
tagCompound.setByte("rsMode", (byte) redstoneMode.ordinal());
}
private void writeBufferToNBT(NBTTagCompound tagCompound) {
NBTTagList bufferTagList = new NBTTagList();
for (int i = 0 ; i < inventoryHelper.getCount() ; i++) {
ItemStack stack = inventoryHelper.getStackInSlot(i);
NBTTagCompound nbtTagCompound = new NBTTagCompound();
if (stack != null) {
stack.writeToNBT(nbtTagCompound);
}
bufferTagList.appendTag(nbtTagCompound);
}
tagCompound.setTag("Items", bufferTagList);
}
}