/* * This file is part of Matter Overdrive * Copyright (c) 2015., Simeon Radivoev, All rights reserved. * * Matter Overdrive is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Matter Overdrive is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Matter Overdrive. If not, see <http://www.gnu.org/licenses>. */ package matteroverdrive.machines.fusionReactorController; import cofh.api.energy.IEnergyConnection; import cofh.api.energy.IEnergyReceiver; import cpw.mods.fml.common.Optional; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; 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.Context; import li.cil.oc.api.network.ManagedPeripheral; import li.cil.oc.api.network.SimpleComponent; import matteroverdrive.api.inventory.UpgradeTypes; import matteroverdrive.data.BlockPos; import matteroverdrive.init.MatterOverdriveBlocks; import matteroverdrive.machines.MachineNBTCategory; import matteroverdrive.machines.fusionReactorController.components.ComponentComputers; import matteroverdrive.multiblock.IMultiBlockTile; import matteroverdrive.multiblock.MultiBlockTileStructureMachine; import matteroverdrive.tile.MOTileEntityMachineEnergy; import matteroverdrive.tile.MOTileEntityMachineMatter; import matteroverdrive.tile.TileEntityFusionReactorPart; import matteroverdrive.tile.TileEntityGravitationalAnomaly; import matteroverdrive.util.MOEnergyHelper; import matteroverdrive.util.TimeTracker; import net.minecraft.block.Block; import net.minecraft.entity.EntityLivingBase; import net.minecraft.init.Blocks; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.MathHelper; import net.minecraft.util.Vec3; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.fluids.Fluid; import java.text.DecimalFormat; import java.util.EnumSet; import static java.lang.Math.round; import static matteroverdrive.util.MOBlockHelper.getAboveSide; import static matteroverdrive.util.MOBlockHelper.getOppositeSide; /** * Created by Simeon on 5/14/2015. */ @Optional.InterfaceList({ @Optional.Interface(modid = "ComputerCraft", iface = "dan200.computercraft.api.peripheral.IPeripheral"), @Optional.Interface(modid = "OpenComputers", iface = "li.cil.oc.api.network.SimpleComponent"), @Optional.Interface(modid = "OpenComputers", iface = "li.cil.oc.api.network.ManagedPeripheral") }) public class TileEntityMachineFusionReactorController extends MOTileEntityMachineMatter implements IPeripheral, SimpleComponent, ManagedPeripheral { public static int STRUCTURE_CHECK_DELAY = 40; public static final int[] positions = new int[]{0,5,1,0,2,0,3,1,4,2,5,3,5,4,5,5,5,6,5,7,4,8,3,9,2,10,1,10,0,10,-1,10,-2,10,-3,9,-4,8,-5,7,-5,6,-5,5,-5,4,-5,3,-4,2,-3,1,-2,0,-1,0}; public static final int[] blocks = new int[]{255,2,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,2}; public static final int positionsCount = positions.length / 2; public static int MAX_GRAVITATIONAL_ANOMALY_DISTANCE = 3; public static int ENERGY_STORAGE = 100000000; public static int MATTER_STORAGE = 2048; public static int ENERGY_PER_TICK = 2048; public static double MATTER_DRAIN_PER_TICK = 1.0D / 80.0D; private boolean validStructure = false; private String monitorInfo = "INVALID STRUCTURE"; private float energyEfficiency; private int energyPerTick; private TimeTracker structureCheckTimer; private BlockPos anomalyPosition; private float matterPerTick; private float matterDrain; private ComponentComputers componentComputers; private MultiBlockTileStructureMachine multiBlock; public TileEntityMachineFusionReactorController() { super(4); structureCheckTimer = new TimeTracker(); energyStorage.setCapacity(ENERGY_STORAGE); energyStorage.setMaxTransfer(ENERGY_STORAGE); energyStorage.setMaxReceive(0); matterStorage.setCapacity(MATTER_STORAGE); matterStorage.setMaxExtract(0); matterStorage.setMaxReceive(MATTER_STORAGE); multiBlock = new MultiBlockTileStructureMachine(this); } @Override public String getSound() { return null; } @Override public void writeCustomNBT(NBTTagCompound nbt, EnumSet<MachineNBTCategory> categories, boolean toDisk) { super.writeCustomNBT(nbt, categories, toDisk); if (categories.contains(MachineNBTCategory.DATA)) { nbt.setBoolean("ValidStructure", validStructure); nbt.setString("MonitorInfo", monitorInfo); nbt.setFloat("EnergyEfficiency", energyEfficiency); nbt.setFloat("MatterPerTick", matterPerTick); nbt.setInteger("EnergyPerTick", energyPerTick); } } @Override protected void onActiveChange() { } @Override public void readCustomNBT(NBTTagCompound nbt, EnumSet<MachineNBTCategory> categories) { super.readCustomNBT(nbt, categories); if (categories.contains(MachineNBTCategory.DATA)) { validStructure = nbt.getBoolean("ValidStructure"); monitorInfo = nbt.getString("MonitorInfo"); energyEfficiency = nbt.getFloat("EnergyEfficiency"); matterPerTick = nbt.getFloat("MatterPerTick"); energyPerTick = nbt.getInteger("EnergyPerTick"); } } @Override protected void onAwake(Side side) { } @Override public void updateEntity() { super.updateEntity(); if (!worldObj.isRemote) { //System.out.println("Fusion Reactor Update in chunk that is loaded:" + worldObj.getChunkFromBlockCoords(xCoord,zCoord).isChunkLoaded); manageStructure(); manageEnergyGeneration(); manageEnergyExtract(); } } @Override protected void registerComponents() { super.registerComponents(); componentComputers = new ComponentComputers(this); addComponent(componentComputers); } @Override public boolean hasSound() { return false; } @Override public boolean getServerActive() { return isValidStructure() && isGeneratingPower(); } @Override public float soundVolume() { return 0; } public Vec3 getPosition(int i,int meta) { if (i < positionsCount) { ForgeDirection back = ForgeDirection.getOrientation(getOppositeSide(meta)); Vec3 pos = Vec3.createVectorHelper(TileEntityMachineFusionReactorController.positions[i * 2], 0, TileEntityMachineFusionReactorController.positions[(i * 2) + 1]); if (back == ForgeDirection.NORTH) { pos.rotateAroundY((float)Math.PI); } else if (back == ForgeDirection.WEST) { pos.rotateAroundY((float)(Math.PI + Math.PI / 2)); } else if (back == ForgeDirection.EAST) { pos.rotateAroundY((float)(Math.PI / 2)); } else if (back == ForgeDirection.UP) { pos.rotateAroundX((float)(Math.PI / 2)); } else if (back == ForgeDirection.DOWN) { pos.rotateAroundX((float) (Math.PI + Math.PI / 2)); } return pos; } return null; } @Override public void invalidate() { super.invalidate(); multiBlock.invalidate(); } public void manageStructure() { if (structureCheckTimer.hasDelayPassed(worldObj, STRUCTURE_CHECK_DELAY)) { multiBlock.update(); int meta = worldObj.getBlockMetadata(xCoord, yCoord, zCoord); int anomalyDistance = MAX_GRAVITATIONAL_ANOMALY_DISTANCE+1; boolean validStructure = true; String info = this.monitorInfo; float energyEfficiency = this.energyEfficiency; float matterPerTick = this.matterPerTick; for (int i = 0; i < positionsCount; i++) { Vec3 offset = getPosition(i, meta); BlockPos position = new BlockPos(xCoord + (int) round(offset.xCoord), yCoord + (int) round(offset.yCoord), zCoord + (int) round(offset.zCoord)); if (blocks[i] == 255) { BlockPos anomalyOffset = checkForGravitationalAnomaly(position, ForgeDirection.getOrientation(getAboveSide(meta))); if (anomalyOffset != null) { anomalyDistance = (int)Math.sqrt((anomalyOffset.x * anomalyOffset.x) + (anomalyOffset.y * anomalyOffset.y) + (anomalyOffset.z * anomalyOffset.z)); if (anomalyDistance > MAX_GRAVITATIONAL_ANOMALY_DISTANCE) { validStructure = false; info = "GRAVITATIONAL\nANOMALY\nTOO\nFAR"; break; } anomalyPosition = new BlockPos((int) offset.xCoord + anomalyOffset.x, (int) offset.yCoord + anomalyOffset.y, (int) offset.zCoord + anomalyOffset.z); }else { validStructure = false; info = "NO\nGRAVITATIONAL\nANOMALY"; anomalyPosition = null; break; } energyEfficiency = 1f - ((float)anomalyDistance / (float)(MAX_GRAVITATIONAL_ANOMALY_DISTANCE+1)); energyPerTick = (int)Math.round(ENERGY_PER_TICK * getEnergyEfficiency() * getGravitationalAnomalyEnergyMultiply()); double energyMultipy = getGravitationalAnomalyEnergyMultiply(); matterPerTick = (float)(MATTER_DRAIN_PER_TICK * energyMultipy); } else { Block block = position.getBlock(worldObj); TileEntity tileEntity = position.getTileEntity(worldObj); if (block == Blocks.air) { validStructure = false; info = "INVALID\nSTRUCTURE"; break; } else if (block == MatterOverdriveBlocks.machine_hull) { if (blocks[i] == 1) { validStructure = false; info = "NEED\nMORE\nCOILS"; break; } } else if (block == MatterOverdriveBlocks.fusion_reactor_coil || tileEntity instanceof IMultiBlockTile) { if (blocks[i] == 0) { validStructure = false; info = "INVALID\nMATERIALS"; break; } } else if (block == MatterOverdriveBlocks.decomposer) { if (blocks[i] != 2) { validStructure = false; info = "INVALID\nMATERIALS"; break; } } else { validStructure = false; info = "INVALID\nMATERIALS"; break; } if (tileEntity instanceof IMultiBlockTile) { multiBlock.addMultiBlockTile((IMultiBlockTile)tileEntity); } } } if (validStructure) { info = "POWER " + Math.round((1f - ((float)anomalyDistance / (float)(MAX_GRAVITATIONAL_ANOMALY_DISTANCE+1))) * 100) + "%"; info += "\nCHARGE " + DecimalFormat.getPercentInstance().format((double)getEnergyStored(ForgeDirection.UNKNOWN)/(double)getMaxEnergyStored(ForgeDirection.UNKNOWN)); info += "\nMATTER " + DecimalFormat.getPercentInstance().format((double)getMatterStored()/(double)getMatterCapacity()); }else { energyEfficiency = 0; } if (this.validStructure != validStructure || !this.monitorInfo.equals(info)|| this.energyEfficiency != energyEfficiency || this.matterPerTick != matterPerTick) { this.validStructure = validStructure; this.monitorInfo = info; this.energyEfficiency = energyEfficiency; this.matterPerTick = matterPerTick; forceSync(); } } } private void manageEnergyGeneration() { if (isActive()) { int energyPerTick = getEnergyPerTick(); int energyRecived = energyStorage.modifyEnergyStored(energyPerTick); if (energyRecived != 0) { matterDrain += getMatterDrainPerTick() * ((float)energyRecived / (float)energyPerTick); if (MathHelper.floor_float(matterDrain) >= 1) { matterStorage.modifyMatterStored(-MathHelper.floor_float(matterDrain)); matterDrain -= MathHelper.floor_float(matterDrain); } } } } private void manageEnergyExtract() { if (energyStorage.getEnergyStored() > 0) { for (IMultiBlockTile tile : multiBlock.getTiles()) { if (tile instanceof TileEntityFusionReactorPart) { manageExtractFrom((TileEntityFusionReactorPart)tile); } } } manageExtractFrom(this); } private void manageExtractFrom(MOTileEntityMachineEnergy source) { TileEntity entity; int energy; int startDir = random.nextInt(6); for (int i = 0; i < 6; i++) { energy = Math.min(energyStorage.getEnergyStored(), ENERGY_STORAGE); ForgeDirection dir = ForgeDirection.getOrientation((i + startDir) % 6); entity = worldObj.getTileEntity(source.xCoord + dir.offsetX, source.yCoord + dir.offsetY, source.zCoord + dir.offsetZ); if (entity instanceof IEnergyConnection) { ((IEnergyConnection) entity).canConnectEnergy(dir.getOpposite()); } if (entity instanceof IEnergyReceiver) { int receivedEnergy = ((IEnergyReceiver) entity).receiveEnergy(dir.getOpposite(), energy, false); modifyEnergyStored(-receivedEnergy); } } } @Override public boolean isCharging() { return this.inventory.getStackInSlot(energySlotID) != null && MOEnergyHelper.isEnergyContainerItem(this.inventory.getStackInSlot(energySlotID)); } @Override protected void manageCharging() { if(isCharging()) { if(!this.worldObj.isRemote) { int maxExtracted = Math.min(energyStorage.getMaxExtract(),energyStorage.getEnergyStored()); int extracted = MOEnergyHelper.insertEnergyIntoContainer(this.inventory.getStackInSlot(energySlotID),maxExtracted,false); modifyEnergyStored(extracted); } } } public int getEnergyPerTick() { return energyPerTick; } public double getGravitationalAnomalyEnergyMultiply() { if (anomalyPosition != null) { TileEntity entity = worldObj.getTileEntity(xCoord + anomalyPosition.x, yCoord + anomalyPosition.y, zCoord + anomalyPosition.z); if (entity instanceof TileEntityGravitationalAnomaly) { return ((TileEntityGravitationalAnomaly) entity).getRealMassUnsuppressed(); } } return 0; } public float getMatterDrainPerTick() { return matterPerTick; } public boolean isGeneratingPower() { return getEnergyEfficiency() > 0 && getEnergyStorage().getEnergyStored() < getEnergyStorage().getMaxEnergyStored() && getMatterStorage().getMatterStored() > getMatterDrainPerTick(); } public float getEnergyEfficiency() { return energyEfficiency; } private BlockPos checkForGravitationalAnomaly(BlockPos position, ForgeDirection up) { int offsetX,offsetY,offsetZ; for (int i = -MAX_GRAVITATIONAL_ANOMALY_DISTANCE; i < MAX_GRAVITATIONAL_ANOMALY_DISTANCE+1;i++) { offsetX = up.offsetX * i; offsetY = up.offsetY * i; offsetZ = up.offsetZ * i; Block block = worldObj.getBlock(position.x + offsetX, position.y + offsetY, position.z + offsetZ); if (block != null && block == MatterOverdriveBlocks.gravitational_anomaly) { return new BlockPos(offsetX, offsetY, offsetZ); } } return null; } public boolean shouldRenderInPass(int pass) { return pass == 1; } @SideOnly(Side.CLIENT) public AxisAlignedBB getRenderBoundingBox() { ForgeDirection backSide = ForgeDirection.getOrientation(getOppositeSide(worldObj.getBlockMetadata(xCoord,yCoord,zCoord))); return AxisAlignedBB.getBoundingBox(xCoord,yCoord,zCoord,xCoord + backSide.offsetX * 10,yCoord + backSide.offsetY * 10,zCoord + backSide.offsetZ * 10); } public boolean isValidStructure() { return validStructure; } public String getMonitorInfo() { return monitorInfo; } @Override public boolean isAffectedByUpgrade(UpgradeTypes type) { return type == UpgradeTypes.PowerStorage || type == UpgradeTypes.Range || type == UpgradeTypes.Speed; } @Override public void onAdded(World world, int x, int y, int z) { } @Override public void onPlaced(World world, EntityLivingBase entityLiving) { } @Override public void onDestroyed() { } @Override public boolean canDrain(ForgeDirection from, Fluid fluid) { return false; } //region All Computers //region ComputerCraft @Override @Optional.Method(modid = "ComputerCraft") public String getType() { return componentComputers.getType(); } @Override @Optional.Method(modid = "ComputerCraft") public String[] getMethodNames() { return componentComputers.getMethodNames(); } @Override @Optional.Method(modid = "ComputerCraft") public Object[] callMethod(IComputerAccess computer, ILuaContext context, int method, Object[] arguments) throws LuaException, InterruptedException { return componentComputers.callMethod(computer,context,method,arguments); } @Override @Optional.Method(modid = "ComputerCraft") public void attach(IComputerAccess computer) {componentComputers.attach(computer);} @Override @Optional.Method(modid = "ComputerCraft") public void detach(IComputerAccess computer) {componentComputers.detach(computer);} @Override @Optional.Method(modid = "ComputerCraft") public boolean equals(IPeripheral other) { return componentComputers.equals(other); } //endregion //region OpenComputers @Override @Optional.Method(modid = "OpenComputers") public String getComponentName() { return componentComputers.getComponentName(); } @Override @Optional.Method(modid = "OpenComputers") public String[] methods() { return componentComputers.methods(); } @Override @Optional.Method(modid = "OpenComputers") public Object[] invoke(String method, Context context, Arguments args) throws Exception { return componentComputers.invoke(method,context,args); } //endregion //endregion }