electrodynamics/src/main/scala/resonantinduction/mechanical/mech/MechanicalNode.java
2014-11-04 22:06:08 +08:00

337 lines
7.7 KiB
Java

package resonantinduction.mechanical.mech;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.common.util.ForgeDirection;
import resonant.api.grid.INode;
import resonant.api.grid.INodeProvider;
import resonant.api.grid.IUpdate;
import resonant.lib.grid.node.NodeConnector;
import resonant.lib.transform.vector.IVectorWorld;
import resonant.lib.utility.nbt.ISaveObj;
import resonantinduction.core.interfaces.IMechanicalNode;
import resonantinduction.core.prefab.node.TMultipartNode;
import java.util.Iterator;
import java.util.Map.Entry;
/**
* Prefab node for the mechanical system used by almost ever mechanical object in Resonant Induction. Handles connections to other tiles, and shares power with them
*
* @author Calclavia, Darkguardsman
*/
//Don't convert to scala as this will find its way into RE later - From Darkguardsman
public class MechanicalNode extends NodeConnector<MechanicalNode> implements TMultipartNode<MechanicalNode>, IMechanicalNode, ISaveObj, IVectorWorld, IUpdate
{
/**
* Marks that the rotation has changed and should be updated client side
*/
public boolean markRotationUpdate = false;
/**
* Makrs that the torque value has changed and should be updated client side
*/
public boolean markTorqueUpdate = false;
/**
* Allows the node to share its power with other nodes
*/
public boolean sharePower = true;
public double torque = 0, prevTorque;
public double prevAngularVelocity, angularVelocity = 0;
/**
* Current angle of rotation, mainly used for rendering
*/
public double renderAngle = 0;
/**
* Angle of rotation of last update
*/
public double prev_angle = 0;
public float acceleration = 2f;
protected double maxDeltaAngle = Math.toRadians(120);
protected double load = 2;
private double power = 0;
/**
* Current update tick #
*/
private long ticks = 0;
public MechanicalNode(INodeProvider parent)
{
super(parent);
}
@Override
public double getRadius(ForgeDirection dir, IMechanicalNode with)
{
return 0.5;
}
@Override
public double getAngularSpeed(ForgeDirection side)
{
return angularVelocity;
}
@Override
public double getForce(ForgeDirection side)
{
return torque;
}
@Override
public boolean inverseRotation(ForgeDirection side)
{
return false;
}
final public void update()
{
update(0.05f);
}
@Override
final public void update(double deltaTime)
{
ticks++;
if (ticks >= Long.MAX_VALUE)
{
ticks = 0;
}
//-----------------------------------
// Render Update
//-----------------------------------
// Updates rotation angle and prevents it from rotating too fast
if (angularVelocity >= 0)
{
renderAngle += Math.min(angularVelocity, this.maxDeltaAngle) * deltaTime;
}
else
{
renderAngle += Math.max(angularVelocity, -this.maxDeltaAngle) * deltaTime;
}
// Cap rotation angle to prevent render issues
if (renderAngle >= Math.PI * 2)
{
revolve();
renderAngle = renderAngle % (Math.PI * 2);
}
//-----------------------------------
// Server side Update
//-----------------------------------
if (world() != null && !world().isRemote)
{
final double acceleration = this.acceleration * deltaTime;
if (Math.abs(prevAngularVelocity - angularVelocity) > 0.01f)
{
prevAngularVelocity = angularVelocity;
markRotationUpdate = true;
}
if (Math.abs(prevTorque - torque) > 0.01f)
{
prevTorque = torque;
markTorqueUpdate = true;
}
//-----------------------------------
// Loss calculations
//-----------------------------------
double torqueLoss = Math.min(Math.abs(getTorque()), (Math.abs(getTorque() * getTorqueLoad()) + getTorqueLoad() / 10) * deltaTime);
torque += torque > 0 ? -torqueLoss : torqueLoss;
double velocityLoss = Math.min(Math.abs(getAngularSpeed()), (Math.abs(getAngularSpeed() * getAngularVelocityLoad()) + getAngularVelocityLoad() / 10) * deltaTime);
angularVelocity += angularVelocity > 0 ? -velocityLoss : velocityLoss;
if (getEnergy() <= 0)
{
angularVelocity = torque = 0;
}
power = getEnergy() / deltaTime;
if (sharePower)
{
// Power sharing calculations
Iterator<Entry<MechanicalNode, ForgeDirection>> it = directionMap().entrySet().iterator();
while (it.hasNext())
{
MechanicalNode adjacentMech = null;
Entry<MechanicalNode, ForgeDirection> entry = it.next();
ForgeDirection dir = entry.getValue();
//TODO: Will never happen that it isn't a mech node.
// Get mech node
if (entry.getKey() instanceof MechanicalNode)
{
adjacentMech = (MechanicalNode) entry.getKey();
}
else if (entry.getKey() instanceof INodeProvider)
{
INode node = ((INodeProvider) entry.getKey()).getNode(MechanicalNode.class, dir.getOpposite());
if (node instanceof MechanicalNode)
{
adjacentMech = (MechanicalNode) node;
}
}
else
{
it.remove();
}
// If node is not null apply power
if (adjacentMech != null)
{
/** Calculate angular velocity and torque. */
double ratio = adjacentMech.getRadius(dir.getOpposite(), this) / getRadius(dir, adjacentMech);
boolean inverseRotation = inverseRotation(dir) && adjacentMech.inverseRotation(dir.getOpposite());
int inversion = inverseRotation ? -1 : 1;
double targetTorque = inversion * adjacentMech.getTorque() / ratio;
double applyTorque = targetTorque * acceleration;
if (Math.abs(torque + applyTorque) < Math.abs(targetTorque))
{
torque += applyTorque;
}
else if (Math.abs(torque - applyTorque) > Math.abs(targetTorque))
{
torque -= applyTorque;
}
double targetVelocity = inversion * adjacentMech.getAngularSpeed() * ratio;
double applyVelocity = targetVelocity * acceleration;
if (Math.abs(angularVelocity + applyVelocity) < Math.abs(targetVelocity))
{
angularVelocity += applyVelocity;
}
else if (Math.abs(angularVelocity - applyVelocity) > Math.abs(targetVelocity))
{
angularVelocity -= applyVelocity;
}
/** Set all current rotations */
// adjacentMech.angle = Math.abs(angle) * (adjacentMech.angle >= 0 ? 1 : -1);
}
}
}
}
onUpdate();
prev_angle = renderAngle;
}
@Override
public boolean canUpdate()
{
return true;
}
@Override
public boolean continueUpdate()
{
return true;
}
protected void onUpdate()
{
}
/**
* Called when one revolution is made.
*/
protected void revolve()
{
}
@Override
public void apply(Object source, double torque, double angularVelocity)
{
this.torque += torque;
this.angularVelocity += angularVelocity;
}
private double getTorque()
{
return angularVelocity != 0 ? torque : 0;
}
private double getAngularSpeed()
{
return torque != 0 ? angularVelocity : 0;
}
/**
* The energy percentage loss due to resistance in seconds.
*/
public double getTorqueLoad()
{
return load;
}
public double getAngularVelocityLoad()
{
return load;
}
public double getEnergy()
{
return getTorque() * getAngularSpeed();
}
public double getPower()
{
return power;
}
@Override
public void load(NBTTagCompound nbt)
{
torque = nbt.getDouble("torque");
angularVelocity = nbt.getDouble("angularVelocity");
}
@Override
public void save(NBTTagCompound nbt)
{
nbt.setDouble("torque", torque);
nbt.setDouble("angularVelocity", angularVelocity);
}
@Override
public <B extends MechanicalNode> boolean canConnect(B other, ForgeDirection from)
{
if (canConnect(from))
{
if (other instanceof INodeProvider)
{
return ((INodeProvider) other).getNode(MechanicalNode.class, from.getOpposite()) instanceof MechanicalNode;
}
return other instanceof MechanicalNode;
}
return false;
}
@Override
public boolean isValidConnection(Object object)
{
return true;
}
}