package mcjty.deepresonance.blocks.collector;
import mcjty.deepresonance.blocks.ModBlocks;
import mcjty.deepresonance.blocks.crystals.ResonatingCrystalTileEntity;
import mcjty.deepresonance.blocks.generator.GeneratorConfiguration;
import mcjty.deepresonance.blocks.generator.GeneratorTileEntity;
import mcjty.deepresonance.config.ConfigMachines;
import mcjty.deepresonance.generatornetwork.DRGeneratorNetwork;
import mcjty.deepresonance.radiation.DRRadiationManager;
import mcjty.lib.entity.GenericTileEntity;
import mcjty.lib.varia.Broadcaster;
import mcjty.lib.varia.GlobalCoordinate;
import mcjty.lib.varia.Logging;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.ITickable;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import java.util.HashSet;
import java.util.Set;
public class EnergyCollectorTileEntity extends GenericTileEntity implements ITickable {
// Minimum power before we stop using a crystal.
public static final float CRYSTAL_MIN_POWER = .00001f;
public static int MAXTICKS = 20; // Update radiation every second.
// Relative coordinates of active crystals.
private Set<BlockPos> crystals = new HashSet<>();
private boolean lasersActive = false;
private int laserStartup = 0; // A mirror (for the client) of the network startup counter.
private int radiationUpdateCount = MAXTICKS;
private int networkID = -1;
public EnergyCollectorTileEntity() {
super();
}
@Override
public void update() {
if (!worldObj.isRemote) {
checkStateServer();
}
}
private void checkStateServer() {
boolean active = false;
int startup = 0;
DRGeneratorNetwork.Network network = null;
TileEntity te = worldObj.getTileEntity(getPos().down());
if (te instanceof GeneratorTileEntity) {
GeneratorTileEntity generatorTileEntity = (GeneratorTileEntity) te;
DRGeneratorNetwork generatorNetwork = DRGeneratorNetwork.getChannels(worldObj);
if (networkID != generatorTileEntity.getNetworkId()) {
if (networkID != -1) {
generatorNetwork.getOrCreateNetwork(networkID).decCollectorBlocks();
}
networkID = generatorTileEntity.getNetworkId();
generatorTileEntity.getNetwork().incCollectorBlocks();
generatorNetwork.save(worldObj);
}
network = generatorTileEntity.getNetwork();
if (network != null) {
if (network.isActive()) {
int rfPerTick = calculateRF();
network.setLastRfPerTick(rfPerTick);
int newEnergy = network.getEnergy() + rfPerTick;
int maxEnergy = network.getGeneratorBlocks() * GeneratorConfiguration.rfPerGeneratorBlock;
if (newEnergy > maxEnergy) {
newEnergy = maxEnergy;
}
if (network.getEnergy() != newEnergy) {
network.setEnergy(newEnergy);
generatorNetwork.save(worldObj);
}
active = true;
} else {
network.setLastRfPerTick(0);
}
startup = network.getStartupCounter();
}
} else {
if (networkID != -1) {
networkID = -1;
markDirty();
}
}
if (active != lasersActive || startup != laserStartup) {
// Only when active changes and the lasert started recently do we check for new crystals.
boolean doFind = lasersActive != active || (laserStartup > (GeneratorConfiguration.startupTime - 5));
lasersActive = active;
laserStartup = startup;
markDirtyClient();
if (doFind && te instanceof GeneratorTileEntity) {
findCrystals(network);
}
}
}
public void disableCrystalGlow() {
for (BlockPos coordinate : crystals) {
TileEntity te = worldObj.getTileEntity(new BlockPos(getPos().getX() + coordinate.getX(), getPos().getY() + coordinate.getY(), getPos().getZ() + coordinate.getZ()));
if (te instanceof ResonatingCrystalTileEntity) {
ResonatingCrystalTileEntity resonatingCrystalTileEntity = (ResonatingCrystalTileEntity) te;
resonatingCrystalTileEntity.setGlowing(false);
}
}
}
private int calculateRF() {
Set<BlockPos> tokeep = new HashSet<>();
boolean dirty = false;
float radiationRadius = 0;
float radiationStrength = 0;
boolean doRadiation = false;
radiationUpdateCount--;
if (radiationUpdateCount <= 0) {
radiationUpdateCount = MAXTICKS;
doRadiation = true;
}
int rf = 0;
for (BlockPos coordinate : crystals) {
TileEntity te = worldObj.getTileEntity(new BlockPos(getPos().getX() + coordinate.getX(), getPos().getY() + coordinate.getY(), getPos().getZ() + coordinate.getZ()));
if (te instanceof ResonatingCrystalTileEntity) {
ResonatingCrystalTileEntity resonatingCrystalTileEntity = (ResonatingCrystalTileEntity) te;
if (resonatingCrystalTileEntity.getPower() > CRYSTAL_MIN_POWER) {
resonatingCrystalTileEntity.setGlowing(lasersActive);
tokeep.add(coordinate);
float power = resonatingCrystalTileEntity.getPower();
if (power < resonatingCrystalTileEntity.getPowerPerTick()) {
// We are empty.
resonatingCrystalTileEntity.setPower(0);
// Crystal will be removed from the list of active crystals next tick.
} else {
power -= resonatingCrystalTileEntity.getPowerPerTick();
resonatingCrystalTileEntity.setPower(power); // @@@ No block update on crystal yet!
int rfPerTick = resonatingCrystalTileEntity.getRfPerTick();
rf += rfPerTick;
if (doRadiation) {
float purity = resonatingCrystalTileEntity.getPurity();
float radius = DRRadiationManager.calculateRadiationRadius(resonatingCrystalTileEntity.getStrength(), resonatingCrystalTileEntity.getEfficiency(), purity);
if (radius > radiationRadius) {
radiationRadius = radius;
}
float strength = DRRadiationManager.calculateRadiationStrength(resonatingCrystalTileEntity.getStrength(), purity);
radiationStrength += strength;
}
}
} else {
resonatingCrystalTileEntity.setGlowing(false);
dirty = true;
}
} else {
dirty = true;
}
}
if (dirty) {
crystals = tokeep;
markDirtyClient();
}
if (doRadiation && radiationRadius > 0.1f) {
DRRadiationManager radiationManager = DRRadiationManager.getManager(worldObj);
GlobalCoordinate thisCoordinate = new GlobalCoordinate(getPos(), worldObj.provider.getDimension());
if (radiationManager.getRadiationSource(thisCoordinate) == null) {
Logging.log("Created radiation source with radius " + radiationRadius + " and strength " + radiationStrength);
}
DRRadiationManager.RadiationSource radiationSource = radiationManager.getOrCreateRadiationSource(thisCoordinate);
radiationSource.update(radiationRadius, radiationStrength, MAXTICKS);
radiationManager.save(worldObj);
}
return rf;
}
public void addCrystal(int x, int y, int z) {
if (networkID == -1) {
return;
}
DRGeneratorNetwork channels = DRGeneratorNetwork.getChannels(worldObj);
DRGeneratorNetwork.Network network = channels.getChannel(networkID);
if (network == null) {
return;
}
int maxSupportedRF = network.getGeneratorBlocks() * GeneratorConfiguration.maxRFInputPerBlock;
for (BlockPos coordinate : crystals) {
TileEntity te = worldObj.getTileEntity(new BlockPos(getPos().getX() + coordinate.getX(), getPos().getY() + coordinate.getY(), getPos().getZ() + coordinate.getZ()));
if (te instanceof ResonatingCrystalTileEntity) {
ResonatingCrystalTileEntity resonatingCrystalTileEntity = (ResonatingCrystalTileEntity) te;
if (resonatingCrystalTileEntity.getPower() > CRYSTAL_MIN_POWER) {
maxSupportedRF -= resonatingCrystalTileEntity.getRfPerTick();
}
}
}
if (addCrystal(x, y, z, network, crystals, crystals, maxSupportedRF) >= 0) {
// Success.
markDirtyClient();
}
}
private static int ERROR_TOOMANYCRYSTALS = -1;
private static int ERROR_TOOMUCHPOWER = -2;
// Returns remaining RF that is supported if crystal could be added. Otherwise one of the errors above.
private int addCrystal(int x, int y, int z, DRGeneratorNetwork.Network network, Set<BlockPos> newCrystals, Set<BlockPos> oldCrystals, int maxSupportedRF) {
int maxSupportedCrystals = network.getGeneratorBlocks() * GeneratorConfiguration.maxCrystalsPerBlock;
TileEntity te = worldObj.getTileEntity(new BlockPos(x, y, z));
if (te instanceof ResonatingCrystalTileEntity) {
ResonatingCrystalTileEntity resonatingCrystalTileEntity = (ResonatingCrystalTileEntity) te;
if (resonatingCrystalTileEntity.getPower() > CRYSTAL_MIN_POWER) {
BlockPos crystalCoordinate = new BlockPos(x - getPos().getX(), y - getPos().getY(), z - getPos().getZ());
if (resonatingCrystalTileEntity.isGlowing() && !oldCrystals.contains(crystalCoordinate)) {
// The crystal is already glowing and is not in our 'old' crystal set. That means that it
// is currently being managed by another generator. We ignore it then.
return maxSupportedRF;
}
if (newCrystals.size() >= maxSupportedCrystals) {
resonatingCrystalTileEntity.setGlowing(false);
return ERROR_TOOMANYCRYSTALS;
} else if (resonatingCrystalTileEntity.getRfPerTick() > maxSupportedRF) {
resonatingCrystalTileEntity.setGlowing(false);
return ERROR_TOOMUCHPOWER;
} else {
maxSupportedRF -= resonatingCrystalTileEntity.getRfPerTick();
newCrystals.add(crystalCoordinate);
resonatingCrystalTileEntity.setGlowing(lasersActive);
}
} else {
resonatingCrystalTileEntity.setGlowing(false);
}
}
return maxSupportedRF;
}
private void findCrystals(DRGeneratorNetwork.Network network) {
Set<BlockPos> newCrystals = new HashSet<>();
int maxSupportedRF = network.getGeneratorBlocks() * GeneratorConfiguration.maxRFInputPerBlock;
boolean tooManyCrystals = false;
boolean tooMuchPower = false;
// @todo this is not a good algorithm. It should find the closest first.
int xCoord = getPos().getX();
int yCoord = getPos().getY();
int zCoord = getPos().getZ();
for (int y = yCoord - ConfigMachines.Collector.maxVerticalCrystalDistance ; y <= yCoord + ConfigMachines.Collector.maxVerticalCrystalDistance ; y++) {
if (y >= 0 && y < worldObj.getHeight()) {
int maxhordist = ConfigMachines.Collector.maxHorizontalCrystalDistance;
for (int x = xCoord - maxhordist; x <= xCoord + maxhordist; x++) {
for (int z = zCoord - maxhordist; z <= zCoord + maxhordist; z++) {
if (worldObj.getBlockState(new BlockPos(x, y, z)).getBlock() == ModBlocks.resonatingCrystalBlock) {
maxSupportedRF = addCrystal(x, y, z, network, newCrystals, crystals, maxSupportedRF);
if (maxSupportedRF == ERROR_TOOMANYCRYSTALS) {
tooManyCrystals = true;
} else if (maxSupportedRF == ERROR_TOOMUCHPOWER) {
tooMuchPower = true;
}
}
}
}
}
}
if (!newCrystals.equals(crystals)) {
crystals = newCrystals;
markDirtyClient();
}
if (lasersActive && (tooManyCrystals || tooMuchPower)) {
// @todo This should be put in the Logging class as a broadcast message
if (tooManyCrystals) {
Broadcaster.broadcast(worldObj, xCoord, yCoord, zCoord, "There are too many crystals for this size generator!", 100);
}
if (tooMuchPower) {
Broadcaster.broadcast(worldObj, xCoord, yCoord, zCoord, "Some crystals are too powerful for this size generator!!", 100);
}
}
}
public Set<BlockPos> getCrystals() {
return crystals;
}
public boolean areLasersActive() {
return lasersActive;
}
public int getLaserStartup() {
return laserStartup;
}
public int getNetworkID() {
return networkID;
}
public void setNetworkID(int networkID) {
this.networkID = networkID;
markDirty();
}
@Override
public void readFromNBT(NBTTagCompound tagCompound) {
super.readFromNBT(tagCompound);
byte[] crystalX = tagCompound.getByteArray("crystalsX");
byte[] crystalY = tagCompound.getByteArray("crystalsY");
byte[] crystalZ = tagCompound.getByteArray("crystalsZ");
crystals.clear();
for (int i = 0 ; i < crystalX.length ; i++) {
crystals.add(new BlockPos(crystalX[i], crystalY[i], crystalZ[i]));
}
lasersActive = tagCompound.getBoolean("lasersActive");
laserStartup = tagCompound.getInteger("laserStartup");
if (tagCompound.hasKey("networkId")) {
networkID = tagCompound.getInteger("networkId");
} else {
networkID = -1;
}
}
@Override
public void writeToNBT(NBTTagCompound tagCompound) {
super.writeToNBT(tagCompound);
byte[] crystalX = new byte[crystals.size()];
byte[] crystalY = new byte[crystals.size()];
byte[] crystalZ = new byte[crystals.size()];
int i = 0;
for (BlockPos crystal : crystals) {
crystalX[i] = (byte) crystal.getX();
crystalY[i] = (byte) crystal.getY();
crystalZ[i] = (byte) crystal.getZ();
i++;
}
tagCompound.setByteArray("crystalsX", crystalX);
tagCompound.setByteArray("crystalsY", crystalY);
tagCompound.setByteArray("crystalsZ", crystalZ);
tagCompound.setBoolean("lasersActive", lasersActive);
tagCompound.setInteger("laserStartup", laserStartup);
tagCompound.setInteger("networkId", networkID);
}
@Override
public boolean shouldRenderInPass(int pass) {
return pass == 1;
}
@SideOnly(Side.CLIENT)
@Override
public AxisAlignedBB getRenderBoundingBox() {
int xCoord = getPos().getX();
int yCoord = getPos().getY();
int zCoord = getPos().getZ();
return new AxisAlignedBB(xCoord - 7, yCoord - 1, zCoord - 7, xCoord + 8, yCoord + 2, zCoord + 8);
}
}