package universalelectricity.electricity; | |
import java.util.ArrayList; | |
import java.util.EnumSet; | |
import java.util.HashMap; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Map; | |
import net.minecraft.src.TileEntity; | |
import net.minecraftforge.common.ForgeDirection; | |
import universalelectricity.Ticker; | |
import universalelectricity.implement.IConductor; | |
import universalelectricity.implement.IElectricityReceiver; | |
import universalelectricity.prefab.TileEntityConductor; | |
import universalelectricity.prefab.Vector3; | |
import cpw.mods.fml.common.TickType; | |
/** | |
* This class is used to manage electricity transferring and flow. It is also used to call updates on UE tile entities. | |
* | |
* Electricity Manager is made for each world so it doesn't conflict with electricity devices in different dimensions. | |
* @author Calclavia | |
* | |
*/ | |
public class ElectricityManager | |
{ | |
/** | |
* ElectricityManager exists on both client and server side. | |
* Rely on the server side one as it is more accurate! Client side only simulates. | |
*/ | |
public static ElectricityManager instance; | |
private List<IElectricityReceiver> electricityReceivers = new ArrayList<IElectricityReceiver>(); | |
private List<IConductor> electricConductors = new ArrayList<IConductor>(); | |
private List<ElectricityTransferData> electricityTransferQueue = new ArrayList<ElectricityTransferData>(); | |
private List<ElectricityNetwork> electricityNetworks = new ArrayList<ElectricityNetwork>(); | |
private int maxConnectionID = 0; | |
public ElectricityManager() | |
{ | |
System.out.println("Universal Electricity's Electricity Manager Initiated."); | |
} | |
/** | |
* Registers an electricity consumer for it to receive electricity. | |
* Call it in your consumer's tile entity constructor like this: | |
* ElectricityManager.registerConsumer(this); | |
* @param newUnit - The consumer to be registered. | |
*/ | |
public void registerElectricUnit(IElectricityReceiver newUnit) | |
{ | |
if (!this.electricityReceivers.contains(newUnit)) | |
{ | |
this.electricityReceivers.add(newUnit); | |
} | |
} | |
/** | |
* Registers a UE conductor | |
* @param conductor - The conductor tile entity | |
* @return - The ID of the connection line that is assigned to this conductor | |
*/ | |
public void registerConductor(TileEntityConductor newConductor) | |
{ | |
cleanUpConnections(); | |
this.electricityNetworks.add(new ElectricityNetwork(getMaxConnectionID(), newConductor)); | |
if (!this.electricConductors.contains(newConductor)) | |
{ | |
this.electricConductors.add(newConductor); | |
} | |
} | |
/** | |
* Merges two connection lines together into one. | |
* @param ID1 - ID of connection line | |
* @param ID2 - ID of connection line | |
*/ | |
public void mergeConnection(int ID1, int ID2) | |
{ | |
if(ID1 != ID2) | |
{ | |
ElectricityNetwork connection1 = getConnectionByID(ID1); | |
ElectricityNetwork connection2 = getConnectionByID(ID2); | |
if(connection1 != null && connection2 != null) | |
{ | |
connection1.conductors.addAll(connection2.conductors); | |
connection1.setID(ID1); | |
this.electricityNetworks.remove(connection2); | |
} | |
else | |
{ | |
System.err.println("Failed to merge Universal Electricity wire connections!"); | |
} | |
} | |
} | |
/** | |
* Separate one connection line into two different ones between two conductors. | |
* This function does this by resetting all wires in the connection line and | |
* making them each reconnect. | |
* @param conductorA - existing conductor | |
* @param conductorB - broken/invalid conductor | |
*/ | |
public void splitConnection(IConductor conductorA, IConductor conductorB) | |
{ | |
ElectricityNetwork connection = getConnectionByID(conductorA.getConnectionID()); | |
connection.cleanUpArray(); | |
for(IConductor conductor : connection.conductors) | |
{ | |
conductor.reset(); | |
} | |
for(IConductor conductor : connection.conductors) | |
{ | |
for (byte i = 0; i < 6; i++) | |
{ | |
conductor.updateConnectionWithoutSplit(Vector3.getConnectorFromSide(conductor.getWorld(), new Vector3(((TileEntity)conductor).xCoord, ((TileEntity)conductor).yCoord, ((TileEntity)conductor).zCoord), ForgeDirection.getOrientation(i)), ForgeDirection.getOrientation(i)); | |
} | |
} | |
} | |
/** | |
* Gets a electricity wire connection line by it's connection ID | |
* @param ID | |
* @return | |
*/ | |
public ElectricityNetwork getConnectionByID(int ID) | |
{ | |
cleanUpConnections(); | |
for (int i = 0; i < this.electricityNetworks.size(); i++) | |
{ | |
if (this.electricityNetworks.get(i).getID() == ID) | |
{ | |
return this.electricityNetworks.get(i); | |
} | |
} | |
return null; | |
} | |
/** | |
* Clean up and remove useless connections | |
*/ | |
public void cleanUpConnections() | |
{ | |
for (int i = 0; i < this.electricityNetworks.size(); i++) | |
{ | |
this.electricityNetworks.get(i).cleanUpArray(); | |
if (this.electricityNetworks.get(i).conductors.size() == 0) | |
{ | |
this.electricityNetworks.remove(i); | |
} | |
} | |
} | |
/** | |
* Get the highest connection ID. Use this to assign new wire connection lines | |
* @return | |
*/ | |
public int getMaxConnectionID() | |
{ | |
this.maxConnectionID ++; | |
return this.maxConnectionID; | |
} | |
/** | |
* Produces electricity into a specific wire which will be distributed across the electricity network. | |
* @param sender - The machine sending the electricity. | |
* @param targetConductor - The conductor receiving the electricity (or connected to the machine). | |
* @param amps - The amount of amps this machine is sending. | |
* @param voltage 0 The amount of volts this machine is sending. | |
*/ | |
public void produceElectricity(TileEntity sender, IConductor targetConductor, double amps, double voltage) | |
{ | |
if(targetConductor != null && amps > 0 && voltage > 0) | |
{ | |
//Find a path between this conductor and all connected units and try to send the electricity to them directly | |
ElectricityNetwork electricityNetwork = this.getConnectionByID(targetConductor.getConnectionID()); | |
if(electricityNetwork != null) | |
{ | |
List<IElectricityReceiver> allElectricUnitsInLine = electricityNetwork.getConnectedReceivers(); | |
double leftOverAmps = amps; | |
for (IConductor conductor : electricityNetwork.conductors) | |
{ | |
for (byte i = 0; i < conductor.getConnectedBlocks().length; i++) | |
{ | |
TileEntity tileEntity = conductor.getConnectedBlocks()[i]; | |
if (tileEntity != null) | |
{ | |
if (tileEntity instanceof IElectricityReceiver) | |
{ | |
IElectricityReceiver receiver = (IElectricityReceiver)tileEntity; | |
if (Math.ceil(receiver.wattRequest()) > 0 && receiver.canReceiveFromSide(ForgeDirection.getOrientation(i).getOpposite())) | |
{ | |
double transferAmps = Math.max(0, Math.min(leftOverAmps, Math.min(amps / allElectricUnitsInLine.size(), ElectricInfo.getAmps(Math.ceil(receiver.wattRequest()), receiver.getVoltage()) ))); | |
leftOverAmps -= transferAmps; | |
//Calculate electricity loss | |
double distance = Vector3.distance(Vector3.get(sender), Vector3.get((TileEntity)receiver)); | |
double ampsReceived = transferAmps - (transferAmps * transferAmps * targetConductor.getResistance() * distance)/voltage; | |
double voltsReceived = voltage - (transferAmps * targetConductor.getResistance() * distance); | |
this.electricityTransferQueue.add(new ElectricityTransferData(sender, receiver, electricityNetwork, ForgeDirection.getOrientation(i).getOpposite(), ampsReceived, voltsReceived)); | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
/** | |
* Checks if the current connection line needs electricity | |
* @return - The amount of watts this connection line needs | |
*/ | |
public double getElectricityRequired(int ID) | |
{ | |
ElectricityNetwork connection = this.getConnectionByID(ID); | |
double need = 0; | |
if (connection != null) | |
{ | |
for (IConductor conductor : connection.conductors) | |
{ | |
for (byte i = 0; i < conductor.getConnectedBlocks().length; i++) | |
{ | |
TileEntity tileEntity = conductor.getConnectedBlocks()[i]; | |
if (tileEntity != null) | |
{ | |
if (tileEntity instanceof IElectricityReceiver) | |
{ | |
IElectricityReceiver electricUnit = (IElectricityReceiver)tileEntity; | |
if (electricUnit.canReceiveFromSide(ForgeDirection.getOrientation(i).getOpposite())) | |
{ | |
need += Math.ceil(electricUnit.wattRequest()); | |
} | |
} | |
} | |
} | |
} | |
} | |
return need; | |
} | |
/** | |
* This function is called to refresh all conductors in UE | |
*/ | |
public void refreshConductors() | |
{ | |
for(int j = 0; j < this.electricConductors.size(); j ++) | |
{ | |
IConductor conductor = this.electricConductors.get(j); | |
conductor.refreshConnectedBlocks(); | |
} | |
} | |
/** | |
* Clean up and remove useless connections | |
*/ | |
public void cleanUpElectricityReceivers() | |
{ | |
try | |
{ | |
for (int i = 0; i < this.electricityReceivers.size(); i++) | |
{ | |
IElectricityReceiver electricUnit = electricityReceivers.get(i); | |
//Cleanup useless units | |
if (electricUnit == null) | |
{ | |
electricityReceivers.remove(electricUnit); | |
} | |
else if (((TileEntity)electricUnit).isInvalid()) | |
{ | |
electricityReceivers.remove(electricUnit); | |
} | |
} | |
} | |
catch(Exception e) | |
{ | |
System.err.println("Failed to clean up electricity receivers."); | |
e.printStackTrace(); | |
} | |
} | |
public void tickStart(EnumSet<TickType> type, Object... tickData) | |
{ | |
try | |
{ | |
HashMap conductorAmpData = new HashMap<ElectricityNetwork, Double>(); | |
for (int i = 0; i < electricityTransferQueue.size(); i ++) | |
{ | |
if(electricityTransferQueue.get(i) != null) | |
{ | |
if(electricityTransferQueue.get(i).isValid()) | |
{ | |
double amps = electricityTransferQueue.get(i).amps; | |
if(conductorAmpData.containsKey(electricityTransferQueue.get(i).network)) | |
{ | |
amps += (Double)conductorAmpData.get(electricityTransferQueue.get(i).network); | |
} | |
conductorAmpData.put(electricityTransferQueue.get(i).network, amps); | |
electricityTransferQueue.get(i).receiver.onReceive(electricityTransferQueue.get(i).sender, electricityTransferQueue.get(i).amps, electricityTransferQueue.get(i).voltage, electricityTransferQueue.get(i).side); | |
} | |
} | |
electricityTransferQueue.remove(i); | |
} | |
Iterator it = conductorAmpData.entrySet().iterator(); | |
while (it.hasNext()) | |
{ | |
Map.Entry pairs = (Map.Entry)it.next(); | |
if(pairs.getKey() != null && pairs.getValue() != null) | |
{ | |
if(pairs.getKey() instanceof ElectricityNetwork && pairs.getValue() instanceof Double) | |
{ | |
if(((Double)pairs.getValue()) > ((ElectricityNetwork)pairs.getKey()).getLowestAmpConductor()) | |
{ | |
((ElectricityNetwork)pairs.getKey()).meltDown(); | |
} | |
} | |
} | |
it.remove(); | |
} | |
} | |
catch(Exception e) | |
{ | |
System.err.println("Failed to transfer electricity to receivers."); | |
e.printStackTrace(); | |
} | |
} | |
public void tickEnd(EnumSet<TickType> type, Object... tickData) | |
{ | |
if(Ticker.inGameTicks == 0) | |
{ | |
this.refreshConductors(); | |
} | |
} | |
} |