/** * Copyright (c) 2011-2015, SpaceToad and the BuildCraft Team * http://www.mod-buildcraft.com * <p/> * BuildCraft is distributed under the terms of the Minecraft Mod Public * License 1.0, or MMPL. Please check the contents of the license located in * http://www.mod-buildcraft.com/MMPL-1.0.txt */ package buildcraft.core.lib.engines; import io.netty.buffer.ByteBuf; import net.minecraft.block.Block; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.Container; import net.minecraft.inventory.ICrafting; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.ResourceLocation; import net.minecraftforge.common.util.ForgeDirection; import cofh.api.energy.IEnergyConnection; import cofh.api.energy.IEnergyHandler; import cofh.api.energy.IEnergyReceiver; import buildcraft.BuildCraftCore; import buildcraft.api.power.IEngine; import buildcraft.api.tiles.IHeatable; import buildcraft.api.tools.IToolWrench; import buildcraft.api.transport.IPipeConnection; import buildcraft.api.transport.IPipeTile; import buildcraft.core.CompatHooks; import buildcraft.core.lib.block.TileBuildCraft; import buildcraft.core.lib.utils.MathUtils; import buildcraft.core.lib.utils.ResourceUtils; import buildcraft.core.lib.utils.Utils; public abstract class TileEngineBase extends TileBuildCraft implements IPipeConnection, IEnergyHandler, IEngine, IHeatable { // TEMP public static final ResourceLocation TRUNK_BLUE_TEXTURE = new ResourceLocation("buildcraftcore:textures/blocks/engine/trunk_blue.png"); public static final ResourceLocation TRUNK_GREEN_TEXTURE = new ResourceLocation("buildcraftcore:textures/blocks/engine/trunk_green.png"); public static final ResourceLocation TRUNK_YELLOW_TEXTURE = new ResourceLocation("buildcraftcore:textures/blocks/engine/trunk_yellow.png"); public static final ResourceLocation TRUNK_RED_TEXTURE = new ResourceLocation("buildcraftcore:textures/blocks/engine/trunk_red.png"); public static final ResourceLocation TRUNK_OVERHEAT_TEXTURE = new ResourceLocation("buildcraftcore:textures/blocks/engine/trunk_overheat.png"); public enum EnergyStage { BLUE, GREEN, YELLOW, RED, OVERHEAT; public static final EnergyStage[] VALUES = values(); } public static final float MIN_HEAT = 20; public static final float IDEAL_HEAT = 100; public static final float MAX_HEAT = 250; public int currentOutput = 0; public boolean isRedstonePowered = false; public float progress; public int energy; public float heat = MIN_HEAT; public EnergyStage energyStage = EnergyStage.BLUE; public ForgeDirection orientation = ForgeDirection.UP; protected int progressPart = 0; private boolean checkOrientation = false; private boolean isPumping = false; // Used for SMP synch public TileEngineBase() { } @Override public void initialize() { if (!worldObj.isRemote) { checkRedstonePower(); } } private String getTexturePrefix() { if (!(blockType instanceof BlockEngineBase)) { Block engineBase = worldObj.getBlock(xCoord, yCoord, zCoord); if (engineBase instanceof BlockEngineBase) { blockType = engineBase; getBlockMetadata(); } else { return null; } } return ((BlockEngineBase) blockType).getTexturePrefix(getBlockMetadata(), true); } public ResourceLocation getBaseTexture() { if (getTexturePrefix() != null) { return new ResourceLocation(getTexturePrefix() + "/base.png"); } else { return new ResourceLocation("missingno"); } } public ResourceLocation getChamberTexture() { if (getTexturePrefix() != null) { return new ResourceLocation(getTexturePrefix() + "/chamber.png"); } else { return new ResourceLocation("missingno"); } } public ResourceLocation getTrunkTexture(EnergyStage stage) { if (getTexturePrefix() == null) { return TRUNK_OVERHEAT_TEXTURE; } if (ResourceUtils.resourceExists(getTexturePrefix() + "/trunk.png")) { return new ResourceLocation(getTexturePrefix() + "/trunk.png"); } switch (stage) { case BLUE: return TRUNK_BLUE_TEXTURE; case GREEN: return TRUNK_GREEN_TEXTURE; case YELLOW: return TRUNK_YELLOW_TEXTURE; case RED: return TRUNK_RED_TEXTURE; case OVERHEAT: return TRUNK_OVERHEAT_TEXTURE; default: return TRUNK_RED_TEXTURE; } } public boolean onBlockActivated(EntityPlayer player, ForgeDirection side) { if (!player.worldObj.isRemote && player.getCurrentEquippedItem() != null && player.getCurrentEquippedItem().getItem() instanceof IToolWrench) { IToolWrench wrench = (IToolWrench) player.getCurrentEquippedItem().getItem(); if (wrench.canWrench(player, xCoord, yCoord, zCoord)) { if (getEnergyStage() == EnergyStage.OVERHEAT && !Utils.isFakePlayer(player)) { energyStage = computeEnergyStage(); sendNetworkUpdate(); } checkOrientation = true; wrench.wrenchUsed(player, xCoord, yCoord, zCoord); return true; } } return false; } public double getEnergyLevel() { return ((double) energy) / getMaxEnergy(); } protected EnergyStage computeEnergyStage() { float energyLevel = getHeatLevel(); if (energyLevel < 0.25f) { return EnergyStage.BLUE; } else if (energyLevel < 0.5f) { return EnergyStage.GREEN; } else if (energyLevel < 0.75f) { return EnergyStage.YELLOW; } else if (energyLevel < 1f) { return EnergyStage.RED; } else { return EnergyStage.OVERHEAT; } } public final EnergyStage getEnergyStage() { if (!worldObj.isRemote) { if (energyStage == EnergyStage.OVERHEAT) { return energyStage; } EnergyStage newStage = computeEnergyStage(); if (energyStage != newStage) { energyStage = newStage; if (energyStage == EnergyStage.OVERHEAT) { overheat(); } sendNetworkUpdate(); } } return energyStage; } public void overheat() { this.isPumping = false; if (BuildCraftCore.canEnginesExplode) { worldObj.createExplosion(null, xCoord, yCoord, zCoord, 3, true); worldObj.setBlockToAir(xCoord, yCoord, zCoord); } } public void updateHeat() { heat = (float) ((MAX_HEAT - MIN_HEAT) * getEnergyLevel()) + MIN_HEAT; } public float getHeatLevel() { return (heat - MIN_HEAT) / (MAX_HEAT - MIN_HEAT); } public float getPistonSpeed() { if (!worldObj.isRemote) { return Math.max(0.16f * getHeatLevel(), 0.01f); } switch (getEnergyStage()) { case BLUE: return 0.02F; case GREEN: return 0.04F; case YELLOW: return 0.08F; case RED: return 0.16F; default: return 0; } } @Override public void updateEntity() { super.updateEntity(); if (worldObj.isRemote) { if (progressPart != 0) { progress += getPistonSpeed(); if (progress > 1) { progressPart = 0; progress = 0; } } else if (this.isPumping) { progressPart = 1; } return; } if (checkOrientation) { checkOrientation = false; if (!isOrientationValid()) { switchOrientation(true); } } updateHeat(); getEnergyStage(); if (getEnergyStage() == EnergyStage.OVERHEAT) { this.energy = Math.max(this.energy - 50, 0); return; } engineUpdate(); Object tile = getEnergyProvider(orientation); if (progressPart != 0) { progress += getPistonSpeed(); if (progress > 0.5 && progressPart == 1) { progressPart = 2; } else if (progress >= 1) { progress = 0; progressPart = 0; } } else if (isRedstonePowered && isActive()) { if (isPoweredTile(tile, orientation)) { progressPart = 1; setPumping(true); if (getPowerToExtract() > 0) { progressPart = 1; setPumping(true); } else { setPumping(false); } } else { setPumping(false); } } else { setPumping(false); } burn(); if (!isRedstonePowered) { currentOutput = 0; } else if (isRedstonePowered && isActive()) { sendPower(); } } public Object getEnergyProvider(ForgeDirection orientation) { return CompatHooks.INSTANCE.getEnergyProvider(getTile(orientation)); } private int getPowerToExtract() { Object tile = getEnergyProvider(orientation); if (tile instanceof IEngine) { IEngine engine = (IEngine) tile; int maxEnergy = engine.receiveEnergyFromEngine( orientation.getOpposite(), this.energy, true); return extractEnergy(maxEnergy, false); } else if (tile instanceof IEnergyHandler) { IEnergyHandler handler = (IEnergyHandler) tile; int maxEnergy = handler.receiveEnergy( orientation.getOpposite(), this.energy, true); return extractEnergy(maxEnergy, false); } else if (tile instanceof IEnergyReceiver) { IEnergyReceiver handler = (IEnergyReceiver) tile; int maxEnergy = handler.receiveEnergy( orientation.getOpposite(), this.energy, true); return extractEnergy(maxEnergy, false); } else { return 0; } } protected void sendPower() { Object tile = getEnergyProvider(orientation); if (isPoweredTile(tile, orientation)) { int extracted = getPowerToExtract(); if (extracted <= 0) { setPumping(false); return; } setPumping(true); if (tile instanceof IEngine) { IEngine engine = (IEngine) tile; int neededRF = engine.receiveEnergyFromEngine( orientation.getOpposite(), extracted, false); extractEnergy(neededRF, true); } else if (tile instanceof IEnergyHandler) { IEnergyHandler handler = (IEnergyHandler) tile; int neededRF = handler.receiveEnergy( orientation.getOpposite(), extracted, false); extractEnergy(neededRF, true); } else if (tile instanceof IEnergyReceiver) { IEnergyReceiver handler = (IEnergyReceiver) tile; int neededRF = handler.receiveEnergy( orientation.getOpposite(), extracted, false); extractEnergy(neededRF, true); } } } protected void burn() { } protected void engineUpdate() { if (!isRedstonePowered) { if (energy >= 10) { energy -= 10; } else if (energy < 10) { energy = 0; } } } public boolean isActive() { return true; } protected final void setPumping(boolean isActive) { if (this.isPumping == isActive) { return; } this.isPumping = isActive; sendNetworkUpdate(); } public boolean isOrientationValid() { Object tile = getEnergyProvider(orientation); return isPoweredTile(tile, orientation); } public boolean switchOrientation(boolean preferPipe) { if (preferPipe && switchOrientationDo(true)) { return true; } else { return switchOrientationDo(false); } } private boolean switchOrientationDo(boolean pipesOnly) { for (int i = orientation.ordinal() + 1; i <= orientation.ordinal() + 6; ++i) { ForgeDirection o = ForgeDirection.VALID_DIRECTIONS[i % 6]; Object tile = getEnergyProvider(o); if ((!pipesOnly || tile instanceof IPipeTile) && isPoweredTile(tile, o)) { orientation = o; worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord, zCoord, worldObj.getBlock(xCoord, yCoord, zCoord)); return true; } } return false; } @Override public void invalidate() { super.invalidate(); checkOrientation = true; } @Override public void validate() { super.validate(); checkOrientation = true; } @Override public void readFromNBT(NBTTagCompound data) { super.readFromNBT(data); orientation = ForgeDirection.getOrientation(data.getByte("orientation")); progress = data.getFloat("progress"); energy = data.getInteger("energy"); heat = data.getFloat("heat"); } @Override public void writeToNBT(NBTTagCompound data) { super.writeToNBT(data); data.setByte("orientation", (byte) orientation.ordinal()); data.setFloat("progress", progress); data.setInteger("energy", energy); data.setFloat("heat", heat); } @Override public void readData(ByteBuf stream) { int flags = stream.readUnsignedByte(); energyStage = EnergyStage.values()[flags & 0x07]; isPumping = (flags & 0x08) != 0; orientation = ForgeDirection.getOrientation(stream.readByte()); } @Override public void writeData(ByteBuf stream) { stream.writeByte(energyStage.ordinal() | (isPumping ? 8 : 0)); stream.writeByte(orientation.ordinal()); } public void getGUINetworkData(int id, int value) { switch (id) { case 0: energy = (energy & 0xffff0000) | (value & 0xffff); break; case 1: energy = (energy & 0xffff) | ((value & 0xffff) << 16); break; case 2: currentOutput = value; break; case 3: heat = value / 100F; break; } } public void sendGUINetworkData(Container container, ICrafting iCrafting) { iCrafting.sendProgressBarUpdate(container, 0, energy & 0xffff); iCrafting.sendProgressBarUpdate(container, 1, (energy & 0xffff0000) >> 16); iCrafting.sendProgressBarUpdate(container, 2, currentOutput); iCrafting.sendProgressBarUpdate(container, 3, Math.round(heat * 100)); } /* STATE INFORMATION */ public abstract boolean isBurning(); public void addEnergy(int addition) { if (getEnergyStage() == EnergyStage.OVERHEAT) { return; } energy += addition; if (energy > getMaxEnergy()) { energy = getMaxEnergy(); } } public int extractEnergy(int energyMax, boolean doExtract) { int max = Math.min(energyMax, getCurrentOutputLimit()); int extracted; if (energy >= max) { extracted = max; if (doExtract) { energy -= max; } } else { extracted = energy; if (doExtract) { energy = 0; } } return extracted; } public boolean isPoweredTile(Object tile, ForgeDirection side) { if (tile == null) { return false; } else if (tile instanceof IEngine) { return ((IEngine) tile).canReceiveFromEngine(side.getOpposite()); } else if (tile instanceof IEnergyHandler || tile instanceof IEnergyReceiver) { return ((IEnergyConnection) tile).canConnectEnergy(side.getOpposite()); } else { return false; } } public abstract int getMaxEnergy(); public int getEnergyStored() { return energy; } public abstract int getIdealOutput(); public int getCurrentOutputLimit() { return Integer.MAX_VALUE; } @Override public ConnectOverride overridePipeConnection(IPipeTile.PipeType type, ForgeDirection with) { if (type == IPipeTile.PipeType.POWER) { return ConnectOverride.DEFAULT; } else if (with == orientation) { return ConnectOverride.DISCONNECT; } else { return ConnectOverride.DEFAULT; } } public void checkRedstonePower() { isRedstonePowered = worldObj.isBlockIndirectlyGettingPowered(xCoord, yCoord, zCoord); } public void onNeighborUpdate() { checkRedstonePower(); checkOrientation = true; } // RF support @Override public int extractEnergy(ForgeDirection from, int maxExtract, boolean simulate) { return 0; } @Override public int getEnergyStored(ForgeDirection from) { if (!(from == orientation)) { return 0; } return energy; } @Override public int getMaxEnergyStored(ForgeDirection from) { return this.getMaxEnergy(); } @Override public boolean canConnectEnergy(ForgeDirection from) { return from == orientation; } // IEngine @Override public boolean canReceiveFromEngine(ForgeDirection side) { return side == orientation.getOpposite(); } @Override public int receiveEnergyFromEngine(ForgeDirection side, int amount, boolean simulate) { if (canReceiveFromEngine(side)) { int targetEnergy = Math.min(this.getMaxEnergy() - this.energy, amount); if (!simulate) { energy += targetEnergy; } return targetEnergy; } else { return 0; } } // IHeatable @Override public double getMinHeatValue() { return MIN_HEAT; } @Override public double getIdealHeatValue() { return IDEAL_HEAT; } @Override public double getMaxHeatValue() { return MAX_HEAT; } @Override public double getCurrentHeatValue() { return heat; } @Override public double setHeatValue(double value) { heat = (float) MathUtils.clamp(value, MIN_HEAT, MAX_HEAT); return heat; } }