| /** |
| * Copyright (c) SpaceToad, 2011 http://www.mod-buildcraft.com |
| * |
| * 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.api.power; |
| |
| import buildcraft.api.core.SafeTimeTracker; |
| import net.minecraft.nbt.NBTTagCompound; |
| import net.minecraftforge.common.ForgeDirection; |
| |
| /** |
| * The PowerHandler is similar to FluidTank in that it holds your power and |
| * allows standardized interaction between machines. |
| * |
| * To receive power to your machine you needs create an instance of PowerHandler |
| * and implement IPowerReceptor on the TileEntity. |
| * |
| * If you plan emit power, you need only implement IPowerEmitter. You do not |
| * need a PowerHandler. Engines have a PowerHandler because they can also |
| * receive power from other Engines. |
| * |
| * See TileRefinery for a simple example of a power using machine. |
| * |
| * @see IPowerReceptor |
| * @see IPowerEmitter |
| * |
| * @author CovertJaguar <http://www.railcraft.info/> |
| */ |
| public final class PowerHandler { |
| |
| public static enum Type { |
| |
| ENGINE, GATE, MACHINE, PIPE, STORAGE; |
| |
| public boolean canReceiveFromPipes() { |
| switch (this) { |
| case MACHINE: |
| case STORAGE: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| public boolean eatsEngineExcess() { |
| switch (this) { |
| case MACHINE: |
| case STORAGE: |
| return true; |
| default: |
| return false; |
| } |
| } |
| } |
| |
| /** |
| * Extend this class to create custom Perdition algorithms (its not final). |
| * |
| * NOTE: It is not possible to create a Zero perdition algorithm. |
| */ |
| public static class PerditionCalculator { |
| |
| public static final float DEFAULT_POWERLOSS = 1F; |
| public static final float MIN_POWERLOSS = 0.01F; |
| private final float powerLoss; |
| |
| public PerditionCalculator() { |
| powerLoss = DEFAULT_POWERLOSS; |
| } |
| |
| /** |
| * Simple constructor for simple Perdition per tick. |
| * |
| * @param powerLoss power loss per tick |
| */ |
| public PerditionCalculator(float powerLoss) { |
| if (powerLoss < MIN_POWERLOSS) { |
| powerLoss = MIN_POWERLOSS; |
| } |
| this.powerLoss = powerLoss; |
| } |
| |
| /** |
| * Apply the perdition algorithm to the current stored energy. This |
| * function can only be called once per tick, but it might not be called |
| * every tick. It is triggered by any manipulation of the stored energy. |
| * |
| * @param powerHandler the PowerHandler requesting the perdition update |
| * @param current the current stored energy |
| * @param ticksPassed ticks since the last time this function was called |
| * @return |
| */ |
| public float applyPerdition(PowerHandler powerHandler, float current, long ticksPassed) { |
| // float prev = current; |
| current -= powerLoss * ticksPassed; |
| if (current < 0) { |
| current = 0; |
| } |
| // powerHandler.totalLostPower += prev - current; |
| return current; |
| } |
| |
| /** |
| * Taxes a flat rate on all incoming power. |
| * |
| * Defaults to 0% tax rate. |
| * |
| * @return percent of input to tax |
| */ |
| public float getTaxPercent() { |
| return 0; |
| } |
| } |
| public static final PerditionCalculator DEFAULT_PERDITION = new PerditionCalculator(); |
| private float minEnergyReceived; |
| private float maxEnergyReceived; |
| private float maxEnergyStored; |
| private float activationEnergy; |
| private float energyStored = 0; |
| private final SafeTimeTracker doWorkTracker = new SafeTimeTracker(); |
| private final SafeTimeTracker sourcesTracker = new SafeTimeTracker(); |
| private final SafeTimeTracker perditionTracker = new SafeTimeTracker(); |
| public final int[] powerSources = new int[6]; |
| public final IPowerReceptor receptor; |
| private PerditionCalculator perdition; |
| private final PowerReceiver receiver; |
| private final Type type; |
| // Debug |
| // private double totalLostPower = 0; |
| // private double totalReceivedPower = 0; |
| // private double totalUsedPower = 0; |
| // private long startTime = -1; |
| |
| public PowerHandler(IPowerReceptor receptor, Type type) { |
| this.receptor = receptor; |
| this.type = type; |
| this.receiver = new PowerReceiver(); |
| this.perdition = DEFAULT_PERDITION; |
| } |
| |
| public PowerReceiver getPowerReceiver() { |
| return receiver; |
| } |
| |
| public float getMinEnergyReceived() { |
| return minEnergyReceived; |
| } |
| |
| public float getMaxEnergyReceived() { |
| return maxEnergyReceived; |
| } |
| |
| public float getMaxEnergyStored() { |
| return maxEnergyStored; |
| } |
| |
| public float getActivationEnergy() { |
| return activationEnergy; |
| } |
| |
| public float getEnergyStored() { |
| return energyStored; |
| } |
| |
| /** |
| * Setup your PowerHandler's settings. |
| * |
| * @param minEnergyReceived This is the minimum about of power that will be |
| * accepted by the PowerHandler. This should generally be greater than the |
| * activationEnergy if you plan to use the doWork() callback. Anything |
| * greater than 1 will prevent Redstone Engines from powering this Provider. |
| * @param maxEnergyReceived The maximum amount of power accepted by the |
| * PowerHandler. This should generally be less than 500. Too low and larger |
| * engines will overheat while trying to power the machine. Too high, and |
| * the engines will never warm up. Greater values also place greater strain |
| * on the power net. |
| * @param activationEnergy If the stored energy is greater than this value, |
| * the doWork() callback is called (once per tick). |
| * @param maxStoredEnergy The maximum amount of power this PowerHandler can |
| * store. Values tend to range between 100 and 5000. With 1000 and 1500 |
| * being common. |
| */ |
| public void configure(float minEnergyReceived, float maxEnergyReceived, float activationEnergy, float maxStoredEnergy) { |
| if (minEnergyReceived > maxEnergyReceived) { |
| maxEnergyReceived = minEnergyReceived; |
| } |
| this.minEnergyReceived = minEnergyReceived; |
| this.maxEnergyReceived = maxEnergyReceived; |
| this.maxEnergyStored = maxStoredEnergy; |
| this.activationEnergy = activationEnergy; |
| } |
| |
| /** |
| * Allows you define perdition in terms of loss/ticks. |
| * |
| * This function is mostly for legacy implementations. See |
| * PerditionCalculator for more complex perdition formulas. |
| * |
| * @param powerLoss |
| * @param powerLossRegularity |
| * @see PerditionCalculator |
| */ |
| public void configurePowerPerdition(int powerLoss, int powerLossRegularity) { |
| if (powerLoss == 0 || powerLossRegularity == 0) { |
| perdition = new PerditionCalculator(0); |
| return; |
| } |
| perdition = new PerditionCalculator((float) powerLoss / (float) powerLossRegularity); |
| } |
| |
| /** |
| * Allows you to define a new PerditionCalculator class to handler perdition |
| * calculations. |
| * |
| * For example if you want exponentially increasing loss based on amount |
| * stored. |
| * |
| * @param perdition |
| */ |
| public void setPerdition(PerditionCalculator perdition) { |
| if (perdition == null) |
| perdition = DEFAULT_PERDITION; |
| this.perdition = perdition; |
| } |
| |
| public PerditionCalculator getPerdition() { |
| if (perdition == null) |
| return DEFAULT_PERDITION; |
| return perdition; |
| } |
| |
| /** |
| * Ticks the power handler. You should call this if you can, but its not |
| * required. |
| * |
| * If you don't call it, the possibility exists for some weirdness with the |
| * perdition algorithm and work callback as its possible they will not be |
| * called on every tick they otherwise would be. You should be able to |
| * design around this though if you are aware of the limitations. |
| */ |
| public void update() { |
| // if (startTime == -1) |
| // startTime = receptor.getWorld().getTotalWorldTime(); |
| // else { |
| // long duration = receptor.getWorld().getTotalWorldTime() - startTime; |
| // System.out.printf("Power Stats: %s - Stored: %.2f Gained: %.2f - %.2f/t Lost: %.2f - %.2f/t Used: %.2f - %.2f/t%n", receptor.getClass().getSimpleName(), energyStored, totalReceivedPower, totalReceivedPower / duration, totalLostPower, totalLostPower / duration, totalUsedPower, totalUsedPower / duration); |
| // } |
| |
| applyPerdition(); |
| applyWork(); |
| validateEnergy(); |
| } |
| |
| private void applyPerdition() { |
| if (perditionTracker.markTimeIfDelay(receptor.getWorld(), 1) && energyStored > 0) { |
| float newEnergy = getPerdition().applyPerdition(this, energyStored, perditionTracker.durationOfLastDelay()); |
| if (newEnergy == 0 || newEnergy < energyStored) |
| energyStored = newEnergy; |
| else |
| energyStored = DEFAULT_PERDITION.applyPerdition(this, energyStored, perditionTracker.durationOfLastDelay()); |
| validateEnergy(); |
| } |
| } |
| |
| private void applyWork() { |
| if (energyStored >= activationEnergy) { |
| if (doWorkTracker.markTimeIfDelay(receptor.getWorld(), 1)) { |
| receptor.doWork(this); |
| } |
| } |
| } |
| |
| private void updateSources(ForgeDirection source) { |
| if (sourcesTracker.markTimeIfDelay(receptor.getWorld(), 1)) { |
| for (int i = 0; i < 6; ++i) { |
| powerSources[i] -= sourcesTracker.durationOfLastDelay(); |
| if (powerSources[i] < 0) { |
| powerSources[i] = 0; |
| } |
| } |
| } |
| |
| if (source != null) |
| powerSources[source.ordinal()] = 10; |
| } |
| |
| /** |
| * Extract energy from the PowerHandler. You must call this even if doWork() |
| * triggers. |
| * |
| * @param min |
| * @param max |
| * @param doUse |
| * @return amount used |
| */ |
| public float useEnergy(float min, float max, boolean doUse) { |
| applyPerdition(); |
| |
| float result = 0; |
| |
| if (energyStored >= min) { |
| if (energyStored <= max) { |
| result = energyStored; |
| if (doUse) { |
| energyStored = 0; |
| } |
| } else { |
| result = max; |
| if (doUse) { |
| energyStored -= max; |
| } |
| } |
| } |
| |
| validateEnergy(); |
| |
| // if (doUse) |
| // totalUsedPower += result; |
| |
| return result; |
| } |
| |
| public void readFromNBT(NBTTagCompound data) { |
| readFromNBT(data, "powerProvider"); |
| } |
| |
| public void readFromNBT(NBTTagCompound data, String tag) { |
| NBTTagCompound nbt = data.getCompoundTag(tag); |
| energyStored = nbt.getFloat("storedEnergy"); |
| } |
| |
| public void writeToNBT(NBTTagCompound data) { |
| writeToNBT(data, "powerProvider"); |
| } |
| |
| public void writeToNBT(NBTTagCompound data, String tag) { |
| NBTTagCompound nbt = new NBTTagCompound(); |
| nbt.setFloat("storedEnergy", energyStored); |
| data.setCompoundTag(tag, nbt); |
| } |
| |
| public final class PowerReceiver { |
| |
| private PowerReceiver() { |
| } |
| |
| public float getMinEnergyReceived() { |
| return minEnergyReceived; |
| } |
| |
| public float getMaxEnergyReceived() { |
| return maxEnergyReceived; |
| } |
| |
| public float getMaxEnergyStored() { |
| return maxEnergyStored; |
| } |
| |
| public float getActivationEnergy() { |
| return activationEnergy; |
| } |
| |
| public float getEnergyStored() { |
| return energyStored; |
| } |
| |
| public Type getType() { |
| return type; |
| } |
| |
| public void update() { |
| PowerHandler.this.update(); |
| } |
| |
| /** |
| * The amount of power that this PowerHandler currently needs. |
| * |
| * @return |
| */ |
| public float powerRequest() { |
| update(); |
| return Math.min(maxEnergyReceived, maxEnergyStored - energyStored); |
| } |
| |
| /** |
| * Add power to the PowerReceiver from an external source. |
| * |
| * IPowerEmitters are responsible for calling this themselves. |
| * |
| * @param quantity |
| * @param from |
| * @return the amount of power used |
| */ |
| public float receiveEnergy(Type source, final float quantity, ForgeDirection from) { |
| float used = quantity; |
| if (source == Type.ENGINE) { |
| if (used < minEnergyReceived) { |
| return 0; |
| } else if (used > maxEnergyReceived) { |
| used = maxEnergyReceived; |
| } |
| } |
| |
| updateSources(from); |
| |
| used -= used * getPerdition().getTaxPercent(); |
| |
| used = addEnergy(used); |
| |
| applyWork(); |
| |
| if (source == Type.ENGINE && type.eatsEngineExcess()) { |
| used = Math.min(quantity, maxEnergyReceived); |
| } |
| |
| // totalReceivedPower += used; |
| |
| return used; |
| } |
| } |
| |
| /** |
| * |
| * @return the amount the power changed by |
| */ |
| public float addEnergy(float quantity) { |
| energyStored += quantity; |
| |
| if (energyStored > maxEnergyStored) { |
| quantity -= energyStored - maxEnergyStored; |
| energyStored = maxEnergyStored; |
| } else if (energyStored < 0) { |
| quantity -= energyStored; |
| energyStored = 0; |
| } |
| |
| applyPerdition(); |
| |
| return quantity; |
| } |
| |
| public void setEnergy(float quantity) { |
| this.energyStored = quantity; |
| validateEnergy(); |
| } |
| |
| public boolean isPowerSource(ForgeDirection from) { |
| return powerSources[from.ordinal()] != 0; |
| } |
| |
| private void validateEnergy() { |
| if (energyStored < 0) { |
| energyStored = 0; |
| } |
| if (energyStored > maxEnergyStored) { |
| energyStored = maxEnergyStored; |
| } |
| } |
| } |