package micdoodle8.mods.galacticraft.planets.mars.tile; import io.netty.buffer.ByteBuf; import micdoodle8.mods.galacticraft.api.block.ITerraformableBlock; import micdoodle8.mods.galacticraft.api.tile.IDisableableMachine; import micdoodle8.mods.galacticraft.api.transmission.NetworkType; import micdoodle8.mods.galacticraft.core.energy.item.ItemElectricBase; import micdoodle8.mods.galacticraft.core.energy.tile.TileBaseElectricBlockWithInventory; import micdoodle8.mods.galacticraft.core.entities.IBubbleProvider; import micdoodle8.mods.galacticraft.core.util.ConfigManagerCore; import micdoodle8.mods.galacticraft.core.util.FluidUtil; import micdoodle8.mods.galacticraft.core.util.GCCoreUtil; import micdoodle8.mods.galacticraft.planets.mars.blocks.BlockMachineMars; import micdoodle8.mods.galacticraft.planets.mars.inventory.ContainerTerraformer; import micdoodle8.mods.miccore.Annotations.NetworkedField; import net.minecraft.block.Block; import net.minecraft.block.BlockBush; import net.minecraft.block.BlockSapling; import net.minecraft.init.Blocks; import net.minecraft.init.Items; import net.minecraft.inventory.ISidedInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.BlockPos; import net.minecraft.util.EnumFacing; import net.minecraft.util.IChatComponent; import net.minecraftforge.fluids.*; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import java.util.ArrayList; import java.util.List; public class TileEntityTerraformer extends TileBaseElectricBlockWithInventory implements ISidedInventory, IDisableableMachine, IBubbleProvider, IFluidHandler { private final int tankCapacity = 2000; @NetworkedField(targetSide = Side.CLIENT) public FluidTank waterTank = new FluidTank(this.tankCapacity); public boolean active; public boolean lastActive; public static final int WATTS_PER_TICK = 1; private ItemStack[] containingItems = new ItemStack[14]; private ArrayList<BlockPos> terraformableBlocksList = new ArrayList<BlockPos>(); private ArrayList<BlockPos> grassBlockList = new ArrayList<BlockPos>(); private ArrayList<BlockPos> grownTreesList = new ArrayList<BlockPos>(); @NetworkedField(targetSide = Side.CLIENT) public int terraformableBlocksListSize = 0; // used for server->client ease @NetworkedField(targetSide = Side.CLIENT) public int grassBlocksListSize = 0; // used for server->client ease @NetworkedField(targetSide = Side.CLIENT) public boolean treesDisabled; @NetworkedField(targetSide = Side.CLIENT) public boolean grassDisabled; public final double MAX_SIZE = 15.0D; private int[] useCount = new int[2]; private int saplingIndex = 6; public float bubbleSize; @NetworkedField(targetSide = Side.CLIENT) public boolean shouldRenderBubble = true; public TileEntityTerraformer() { this.storage.setMaxExtract(ConfigManagerCore.hardMode ? 60 : 30); } public int getScaledWaterLevel(int i) { final double fuelLevel = this.waterTank.getFluid() == null ? 0 : this.waterTank.getFluid().amount; return (int) (fuelLevel * i / this.tankCapacity); } @Override public void invalidate() { super.invalidate(); } public double getDistanceFromServer(double par1, double par3, double par5) { final double d3 = this.getPos().getX() + 0.5D - par1; final double d4 = this.getPos().getY() + 0.5D - par3; final double d5 = this.getPos().getZ() + 0.5D - par5; return d3 * d3 + d4 * d4 + d5 * d5; } @Override public void update() { super.update(); // if (this.terraformBubble == null) // { // if (!this.worldObj.isRemote) // { // this.terraformBubble = new EntityTerraformBubble(this.worldObj, new Vector3(this), this); // this.worldObj.spawnEntityInWorld(this.terraformBubble); // } // } if (!this.worldObj.isRemote) { final FluidStack liquid = FluidUtil.getFluidContained(this.containingItems[0]); if (FluidUtil.isFluidStrict(liquid, FluidRegistry.WATER.getName())) { FluidUtil.loadFromContainer(waterTank, FluidRegistry.WATER, containingItems, 0, liquid.amount); } this.active = this.bubbleSize == this.MAX_SIZE && this.hasEnoughEnergyToRun && this.getFirstBonemealStack() != null && this.waterTank.getFluid() != null && this.waterTank.getFluid().amount > 0; } if (!this.worldObj.isRemote && (this.active != this.lastActive || this.ticks % 60 == 0)) { this.terraformableBlocksList.clear(); this.grassBlockList.clear(); if (this.active) { int bubbleSize = (int) Math.ceil(this.bubbleSize); double bubbleSizeSq = this.bubbleSize; bubbleSizeSq *= bubbleSizeSq; boolean doGrass = !this.grassDisabled && this.getFirstSeedStack() != null; boolean doTrees = !this.treesDisabled && this.getFirstSaplingStack() != null; for (int x = this.getPos().getX() - bubbleSize; x < this.getPos().getX() + bubbleSize; x++) { for (int y = this.getPos().getY() - bubbleSize; y < this.getPos().getY() + bubbleSize; y++) { for (int z = this.getPos().getZ() - bubbleSize; z < this.getPos().getZ() + bubbleSize; z++) { BlockPos pos = new BlockPos(x, y, z); Block blockID = this.worldObj.getBlockState(pos).getBlock(); if (blockID == null) { continue; } if (!(blockID.isAir(this.worldObj, pos)) && this.getDistanceFromServer(x, y, z) < bubbleSizeSq) { if (doGrass && blockID instanceof ITerraformableBlock && ((ITerraformableBlock) blockID).isTerraformable(this.worldObj, pos)) { this.terraformableBlocksList.add(new BlockPos(x, y, z)); } else if (doTrees) { Block blockIDAbove = this.worldObj.getBlockState(pos.up()).getBlock(); if (blockID == Blocks.grass && blockIDAbove.isAir(this.worldObj, pos.up())) { this.grassBlockList.add(new BlockPos(x, y, z)); } } } } } } } } if (!this.worldObj.isRemote && this.terraformableBlocksList.size() > 0 && this.ticks % 15 == 0) { ArrayList<BlockPos> terraformableBlocks2 = new ArrayList<BlockPos>(this.terraformableBlocksList); int randomIndex = this.worldObj.rand.nextInt(this.terraformableBlocksList.size()); BlockPos vec = terraformableBlocks2.get(randomIndex); if (this.worldObj.getBlockState(vec).getBlock() instanceof ITerraformableBlock) { Block id; switch (this.worldObj.rand.nextInt(40)) { case 0: if (this.worldObj.isBlockFullCube(new BlockPos(vec.getX() - 1, vec.getY(), vec.getZ())) && this.worldObj.isBlockFullCube(new BlockPos(vec.getX() + 1, vec.getY(), vec.getZ())) && this.worldObj.isBlockFullCube(new BlockPos(vec.getX(), vec.getY(), vec.getZ() - 1)) && this.worldObj.isBlockFullCube(new BlockPos(vec.getX(), vec.getY(), vec.getZ() + 1))) { id = Blocks.flowing_water; } else { id = Blocks.grass; } break; default: id = Blocks.grass; break; } this.worldObj.setBlockState(vec, id.getDefaultState()); if (id == Blocks.grass) { this.useCount[0]++; this.waterTank.drain(1, true); this.checkUsage(1); } else if (id == Blocks.flowing_water) { this.checkUsage(2); } } this.terraformableBlocksList.remove(randomIndex); } if (!this.worldObj.isRemote && !this.treesDisabled && this.grassBlockList.size() > 0 && this.ticks % 50 == 0) { int randomIndex = this.worldObj.rand.nextInt(this.grassBlockList.size()); BlockPos vecGrass = grassBlockList.get(randomIndex); if (this.worldObj.getBlockState(vecGrass).getBlock() == Blocks.grass) { BlockPos vecSapling = vecGrass.add(0, 1, 0); ItemStack sapling = this.getFirstSaplingStack(); boolean flag = false; //Attempt to prevent placement too close to other trees for (BlockPos testVec : this.grownTreesList) { if (testVec.distanceSq(vecSapling) < 9) { flag = true; break; } } if (!flag && sapling != null) { Block b = Block.getBlockFromItem(sapling.getItem()); this.worldObj.setBlockState(vecSapling, b.getStateFromMeta(sapling.getItemDamage()), 3); if (b instanceof BlockSapling) { if (this.worldObj.getLightFromNeighbors(vecSapling) >= 9) { ((BlockSapling) b).grow(this.worldObj, vecSapling, this.worldObj.getBlockState(vecSapling), this.worldObj.rand); this.grownTreesList.add(new BlockPos(vecSapling.getX(), vecSapling.getY(), vecSapling.getZ())); } } else if (b instanceof BlockBush) { if (this.worldObj.getLightFromNeighbors(vecSapling) >= 5) //Hammer the update tick a few times to try to get it to grow - it won't always { for (int j = 0; j < 12; j++) { if (this.worldObj.getBlockState(vecSapling).getBlock() == b) { ((BlockBush) b).updateTick(this.worldObj, vecSapling, this.worldObj.getBlockState(vecSapling), this.worldObj.rand); } else { this.grownTreesList.add(new BlockPos(vecSapling.getX(), vecSapling.getY(), vecSapling.getZ())); break; } } } } this.useCount[1]++; this.waterTank.drain(50, true); this.checkUsage(0); } } this.grassBlockList.remove(randomIndex); } if (!this.worldObj.isRemote) { this.terraformableBlocksListSize = this.terraformableBlocksList.size(); this.grassBlocksListSize = this.grassBlockList.size(); } if (this.hasEnoughEnergyToRun && (!this.grassDisabled || !this.treesDisabled)) { this.bubbleSize = (float) Math.min(Math.max(0, this.bubbleSize + 0.1F), this.MAX_SIZE); } else { this.bubbleSize = (float) Math.min(Math.max(0, this.bubbleSize - 0.1F), this.MAX_SIZE); } this.lastActive = this.active; } @Override public void addExtraNetworkedData(List<Object> networkedList) { if (!this.worldObj.isRemote) { networkedList.add(this.bubbleSize); } } @Override public void readExtraNetworkedData(ByteBuf dataStream) { if (this.worldObj.isRemote) { this.bubbleSize = dataStream.readFloat(); } } private void checkUsage(int type) { ItemStack stack = null; if ((this.useCount[0] + this.useCount[1]) % 4 == 0) { stack = this.getFirstBonemealStack(); if (stack != null) { stack.stackSize--; if (stack.stackSize <= 0) { this.containingItems[this.getSelectiveStack(2, 6)] = null; } } } switch (type) { case 0: stack = this.containingItems[this.saplingIndex]; if (stack != null) { stack.stackSize--; if (stack.stackSize <= 0) { this.containingItems[this.saplingIndex] = null; } } break; case 1: if (this.useCount[0] % 4 == 0) { stack = this.getFirstSeedStack(); if (stack != null) { stack.stackSize--; if (stack.stackSize <= 0) { this.containingItems[this.getSelectiveStack(10, 14)] = null; } } } break; case 2: this.waterTank.drain(50, true); break; } } private int getSelectiveStack(int start, int end) { for (int i = start; i < end; i++) { ItemStack stack = this.containingItems[i]; if (stack != null) { return i; } } return -1; } private int getRandomStack(int start, int end) { int stackcount = 0; for (int i = start; i < end; i++) { if (this.containingItems[i] != null) { stackcount++; } } if (stackcount == 0) { return -1; } int random = this.worldObj.rand.nextInt(stackcount); for (int i = start; i < end; i++) { if (this.containingItems[i] != null) { if (random == 0) { return i; } random--; } } return -1; } public ItemStack getFirstBonemealStack() { int index = this.getSelectiveStack(2, 6); if (index != -1) { return this.containingItems[index]; } return null; } public ItemStack getFirstSaplingStack() { int index = this.getRandomStack(6, 10); if (index != -1) { this.saplingIndex = index; return this.containingItems[index]; } return null; } public ItemStack getFirstSeedStack() { int index = this.getSelectiveStack(10, 14); if (index != -1) { return this.containingItems[index]; } return null; } @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); this.containingItems = this.readStandardItemsFromNBT(nbt); this.bubbleSize = nbt.getFloat("BubbleSize"); this.useCount = nbt.getIntArray("UseCountArray"); if (this.useCount.length == 0) { this.useCount = new int[2]; } if (nbt.hasKey("waterTank")) { this.waterTank.readFromNBT(nbt.getCompoundTag("waterTank")); } if (nbt.hasKey("bubbleVisible")) { this.setBubbleVisible(nbt.getBoolean("bubbleVisible")); } } @Override public void writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); this.writeStandardItemsToNBT(nbt); nbt.setFloat("BubbleSize", this.bubbleSize); nbt.setIntArray("UseCountArray", this.useCount); if (this.waterTank.getFluid() != null) { nbt.setTag("waterTank", this.waterTank.writeToNBT(new NBTTagCompound())); } nbt.setBoolean("bubbleVisible", this.shouldRenderBubble); } @Override public ItemStack[] getContainingItems() { return this.containingItems; } @Override public String getName() { return GCCoreUtil.translate("container.tile_terraformer.name"); } // ISidedInventory Implementation: @Override public int[] getSlotsForFace(EnumFacing side) { return new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; } @Override public boolean canInsertItem(int slotID, ItemStack itemstack, EnumFacing side) { return this.isItemValidForSlot(slotID, itemstack); } @Override public boolean canExtractItem(int slotID, ItemStack itemstack, EnumFacing side) { if (slotID == 0) { return FluidContainerRegistry.isEmptyContainer(itemstack); } if (slotID == 1) { return ((ItemElectricBase) itemstack.getItem()).getElectricityStored(itemstack) <= 0; } return false; } @Override public boolean hasCustomName() { return true; } @Override public boolean isItemValidForSlot(int slotID, ItemStack itemstack) { switch (slotID) { case 0: return FluidContainerRegistry.containsFluid(itemstack, new FluidStack(FluidRegistry.WATER, 1)); case 1: return ItemElectricBase.isElectricItem(itemstack.getItem()); case 2: case 3: case 4: case 5: return itemstack.getItem() == Items.dye && itemstack.getItemDamage() == 15; case 6: case 7: case 8: case 9: return ContainerTerraformer.isOnSaplingList(itemstack); case 10: case 11: case 12: case 13: return itemstack.getItem() == Items.wheat_seeds; } return false; } @Override public boolean shouldUseEnergy() { return !this.grassDisabled || !this.treesDisabled; } @Override public ItemStack getBatteryInSlot() { return this.getStackInSlot(1); } @Override public void setDisabled(int index, boolean disabled) { if (this.disableCooldown <= 0) { switch (index) { case 0: this.treesDisabled = !this.treesDisabled; break; case 1: this.grassDisabled = !this.grassDisabled; break; } this.disableCooldown = 10; } } @Override public boolean getDisabled(int index) { switch (index) { case 0: return this.treesDisabled; case 1: return this.grassDisabled; } return false; } // @Override // public IBubble getBubble() // { // return this.terraformBubble; // } @Override public void setBubbleVisible(boolean shouldRender) { this.shouldRenderBubble = shouldRender; } @Override public double getPacketRange() { return 64.0F; } //Pipe handling @Override public boolean canDrain(EnumFacing from, Fluid fluid) { return false; } @Override public FluidStack drain(EnumFacing from, FluidStack resource, boolean doDrain) { return null; } @Override public FluidStack drain(EnumFacing from, int maxDrain, boolean doDrain) { return null; } @Override public boolean canFill(EnumFacing from, Fluid fluid) { return (fluid == null || "water".equals(fluid.getName())) && from != this.getElectricInputDirection(); } @Override public int fill(EnumFacing from, FluidStack resource, boolean doFill) { int used = 0; if (resource != null && this.canFill(from, resource.getFluid())) { used = this.waterTank.fill(resource, doFill); } return used; } @Override public FluidTankInfo[] getTankInfo(EnumFacing from) { return new FluidTankInfo[] { new FluidTankInfo(this.waterTank) }; } @Override public IChatComponent getDisplayName() { return null; } @Override @SideOnly(Side.CLIENT) public AxisAlignedBB getRenderBoundingBox() { return AxisAlignedBB.fromBounds(this.getPos().getX() - this.bubbleSize, this.getPos().getY() - this.bubbleSize, this.getPos().getZ() - this.bubbleSize, this.getPos().getX() + this.bubbleSize, this.getPos().getY() + this.bubbleSize, this.getPos().getZ() + this.bubbleSize); } @Override public float getBubbleSize() { return this.bubbleSize; } @Override public boolean getBubbleVisible() { return this.shouldRenderBubble; } @Override public EnumFacing getFront() { return this.worldObj.getBlockState(getPos()).getValue(BlockMachineMars.FACING); } @Override public EnumFacing getElectricInputDirection() { return getFront().rotateY(); } @Override public boolean canConnect(EnumFacing direction, NetworkType type) { if (direction == null) { return false; } if (type == NetworkType.POWER) { return direction == this.getElectricInputDirection(); } if (type == NetworkType.FLUID) { return direction != this.getElectricInputDirection(); } return false; } }