494 lines
12 KiB
Java
494 lines
12 KiB
Java
/**
|
|
* Copyright (c) 2011-2014, SpaceToad and the BuildCraft Team
|
|
* 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 net.minecraft.nbt.NBTTagCompound;
|
|
|
|
import net.minecraftforge.common.util.ForgeDirection;
|
|
|
|
import buildcraft.api.core.SafeTimeTracker;
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
@Deprecated
|
|
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 double powerLoss;
|
|
|
|
public PerditionCalculator() {
|
|
powerLoss = DEFAULT_POWERLOSS;
|
|
}
|
|
|
|
/**
|
|
* Simple constructor for simple Perdition per tick.
|
|
*
|
|
* @param powerLoss power loss per tick
|
|
*/
|
|
public PerditionCalculator(double iPowerLoss) {
|
|
if (iPowerLoss < MIN_POWERLOSS) {
|
|
powerLoss = iPowerLoss;
|
|
} else {
|
|
powerLoss = MIN_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 double applyPerdition(PowerHandler powerHandler, double current, long ticksPassed) {
|
|
double result = current;
|
|
|
|
result -= powerLoss * ticksPassed;
|
|
if (result < 0) {
|
|
result = 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Taxes a flat rate on all incoming power.
|
|
*
|
|
* Defaults to 0% tax rate.
|
|
*
|
|
* @return percent of input to tax
|
|
*/
|
|
public double getTaxPercent() {
|
|
return 0;
|
|
}
|
|
}
|
|
public static final PerditionCalculator DEFAULT_PERDITION = new PerditionCalculator();
|
|
public static final double ROLLING_AVERAGE_WEIGHT = 100.0;
|
|
public static final double ROLLING_AVERAGE_NUMERATOR = ROLLING_AVERAGE_WEIGHT - 1;
|
|
public static final double ROLLING_AVERAGE_DENOMINATOR = 1.0 / ROLLING_AVERAGE_WEIGHT;
|
|
public final int[] powerSources = new int[6];
|
|
public final IPowerReceptor receptor;
|
|
private double minEnergyReceived;
|
|
private double maxEnergyReceived;
|
|
private double maxEnergyStored;
|
|
private double activationEnergy;
|
|
private double energyStored = 0;
|
|
private final SafeTimeTracker doWorkTracker = new SafeTimeTracker();
|
|
private final SafeTimeTracker sourcesTracker = new SafeTimeTracker();
|
|
private final SafeTimeTracker perditionTracker = new SafeTimeTracker();
|
|
private PerditionCalculator perdition;
|
|
private final PowerReceiver receiver;
|
|
private final Type type;
|
|
// Tracking
|
|
private double averageLostPower = 0;
|
|
private double averageReceivedPower = 0;
|
|
private double averageUsedPower = 0;
|
|
|
|
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 double getMinEnergyReceived() {
|
|
return minEnergyReceived;
|
|
}
|
|
|
|
public double getMaxEnergyReceived() {
|
|
return maxEnergyReceived;
|
|
}
|
|
|
|
public double getMaxEnergyStored() {
|
|
return maxEnergyStored;
|
|
}
|
|
|
|
public double getActivationEnergy() {
|
|
return activationEnergy;
|
|
}
|
|
|
|
public double 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 iMaxEnergyReceived
|
|
* 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(double iMinEnergyReceived, double iMaxEnergyReceived, double iActivationEnergy,
|
|
double iMaxStoredEnergy) {
|
|
|
|
if (iMinEnergyReceived > maxEnergyReceived) {
|
|
maxEnergyReceived = iMinEnergyReceived;
|
|
} else {
|
|
maxEnergyReceived = iMaxEnergyReceived;
|
|
}
|
|
|
|
minEnergyReceived = iMinEnergyReceived;
|
|
maxEnergyStored = iMaxStoredEnergy;
|
|
activationEnergy = iActivationEnergy;
|
|
}
|
|
|
|
/**
|
|
* 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 iPerdition
|
|
*/
|
|
public void setPerdition(PerditionCalculator iPerdition) {
|
|
if (iPerdition == null) {
|
|
perdition = DEFAULT_PERDITION;
|
|
} else {
|
|
perdition = iPerdition;
|
|
}
|
|
}
|
|
|
|
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() {
|
|
applyPerdition();
|
|
applyWork();
|
|
validateEnergy();
|
|
}
|
|
|
|
private void applyPerdition() {
|
|
if (perditionTracker.markTimeIfDelay(receptor.getWorld(), 1) && energyStored > 0) {
|
|
double prev = energyStored;
|
|
double newEnergy = getPerdition().applyPerdition(this, energyStored, perditionTracker.durationOfLastDelay());
|
|
if (newEnergy == 0 || newEnergy < energyStored) {
|
|
energyStored = newEnergy;
|
|
} else {
|
|
energyStored = DEFAULT_PERDITION.applyPerdition(this, energyStored, perditionTracker.durationOfLastDelay());
|
|
}
|
|
validateEnergy();
|
|
|
|
averageLostPower = (averageLostPower * ROLLING_AVERAGE_NUMERATOR + (prev - energyStored)) * ROLLING_AVERAGE_DENOMINATOR;
|
|
}
|
|
}
|
|
|
|
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 double useEnergy(double min, double max, boolean doUse) {
|
|
applyPerdition();
|
|
|
|
double result = 0;
|
|
|
|
if (energyStored >= min) {
|
|
if (energyStored <= max) {
|
|
result = energyStored;
|
|
if (doUse) {
|
|
energyStored = 0;
|
|
}
|
|
} else {
|
|
result = max;
|
|
if (doUse) {
|
|
energyStored -= max;
|
|
}
|
|
}
|
|
}
|
|
|
|
validateEnergy();
|
|
|
|
if (doUse) {
|
|
averageUsedPower = (averageUsedPower * ROLLING_AVERAGE_NUMERATOR + result) * ROLLING_AVERAGE_DENOMINATOR;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public void readFromNBT(NBTTagCompound data) {
|
|
readFromNBT(data, "powerProvider");
|
|
}
|
|
|
|
public void readFromNBT(NBTTagCompound data, String tag) {
|
|
NBTTagCompound nbt = data.getCompoundTag(tag);
|
|
energyStored = nbt.getDouble("energyStored");
|
|
}
|
|
|
|
public void writeToNBT(NBTTagCompound data) {
|
|
writeToNBT(data, "powerProvider");
|
|
}
|
|
|
|
public void writeToNBT(NBTTagCompound data, String tag) {
|
|
NBTTagCompound nbt = new NBTTagCompound();
|
|
nbt.setDouble("energyStored", energyStored);
|
|
data.setTag(tag, nbt);
|
|
}
|
|
|
|
public final class PowerReceiver {
|
|
|
|
private PowerReceiver() {
|
|
}
|
|
|
|
public double getMinEnergyReceived() {
|
|
return minEnergyReceived;
|
|
}
|
|
|
|
public double getMaxEnergyReceived() {
|
|
return maxEnergyReceived;
|
|
}
|
|
|
|
public double getMaxEnergyStored() {
|
|
return maxEnergyStored;
|
|
}
|
|
|
|
public double getActivationEnergy() {
|
|
return activationEnergy;
|
|
}
|
|
|
|
public double getEnergyStored() {
|
|
return energyStored;
|
|
}
|
|
|
|
public double getAveragePowerReceived() {
|
|
return averageReceivedPower;
|
|
}
|
|
|
|
public double getAveragePowerUsed() {
|
|
return averageUsedPower;
|
|
}
|
|
|
|
public double getAveragePowerLost() {
|
|
return averageLostPower;
|
|
}
|
|
|
|
public Type getType() {
|
|
return type;
|
|
}
|
|
|
|
public void update() {
|
|
PowerHandler.this.update();
|
|
}
|
|
|
|
/**
|
|
* The amount of power that this PowerHandler currently needs.
|
|
*
|
|
* @return
|
|
*/
|
|
public double 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 double receiveEnergy(Type source, final double quantity, ForgeDirection from) {
|
|
double 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);
|
|
}
|
|
|
|
averageReceivedPower = (averageReceivedPower * ROLLING_AVERAGE_NUMERATOR + used) * ROLLING_AVERAGE_DENOMINATOR;
|
|
|
|
return used;
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @return the amount the power changed by
|
|
*/
|
|
public double addEnergy(double iQuantity) {
|
|
energyStored += iQuantity;
|
|
|
|
double added = iQuantity;
|
|
|
|
if (energyStored > maxEnergyStored) {
|
|
added -= energyStored - maxEnergyStored;
|
|
energyStored = maxEnergyStored;
|
|
} else if (energyStored < 0) {
|
|
added -= energyStored;
|
|
energyStored = 0;
|
|
}
|
|
|
|
applyPerdition();
|
|
|
|
return added;
|
|
}
|
|
|
|
public void setEnergy(double 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;
|
|
}
|
|
}
|
|
}
|