package com.dynious.biota.biosystem;
import com.dynious.biota.Biota;
import com.dynious.biota.config.BiomeConfig;
import com.dynious.biota.lib.Settings;
import com.dynious.biota.network.NetworkHandler;
import com.dynious.biota.network.message.MessageBioSystemUpdate;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraft.world.chunk.Chunk;
import java.lang.ref.WeakReference;
import java.util.Random;
public class BioSystem
{
private static final Random RANDOM = new Random();
public final WeakReference<Chunk> chunkReference;
private int tick = RANDOM.nextInt(Settings.TICKS_PER_BIOSYSTEM_UPDATE);
/**
* Stores the amount of plants in the chunk. Plant blocks can have different amounts 'plant value'.
*/
private float biomass;
/**
* Stores the amount of nitrogen fixated by the plants in this chunk.
*/
private float nitrogenFixation;
/**
* Phosphorus is used by plants for growth. Plants and animal waste will be turned into phosphates by bacteria when decomposed.
* Phosphorus is usually the limiting factor in plant growth. It can be released slowly by weathering of rock but
* can also be incorporated into rock. Bone meal has lots of phosphorus!
*
* 10 - 20 ppm normal
* Normal Carbon:Phosphorus rate = 200:1 - 300:1
*/
private float phosphorus;
/**
* Potassium is essential for plants and will affect plants greatly when there's not enough available in the soil.
* Plants will take up more than needed for healthy growth if there's enough available. Works a lot like phosphorus.
* Bone meal has no potassium. Potassium can be gained by mining rock.
*
* 150 - 250 ppm K normal.
*/
private float potassium;
/**
* Nitrate is used by plants for growth. Plants and animal waste will be turned into ammonia by bacteria when decomposed.
* This ammonia will be turned into nitrate by nitrifying bacteria. Nitrate can be removed from the ground and into
* the atmosphere by denitrifying bacteria. Ammonia can be inserted in the ground from the atmosphere by nitrogen-fixing
* soil bacteria.
*
* 5 - 10 ppm normal. 25+ ppm optimal
*/
private float nitrogen;
/**
* Bacteria that decompose dead stuff. When there are not enough of these bacteria, the nutrients in dead plants
* and animals will not fully be reinserted into the biosystem. The amount needed depends on the amount of plants
* and animals in the chunk.
*/
private float decomposingBacteria;
/**
* Nitrifying bacteria are needed to convert the ammonia created by the Decomposing Bacteria to Nitrate used by
* plants to grow. When there are not enough of these bacteria the amount of nitrogen will Nitrate in the soil
* will slowly decline, causing growth issues.
*/
private float nitrifyingBacteria;
/*
Soil density: 1360 kg/m^3
Around 768 dirt blocks per chunk (calculate this? biome dependant?) (16*16*3)
1044480 kg of dirt per chunk
We assume all parts in soil weigh the same so: 1.04448 kg per millionth part (for ppm)
1 ppm ~= 1 kg
*/
public BioSystem(Chunk chunk)
{
this(chunk, BiomeConfig.getRandomizedNutrientValuesForChunk(chunk));
}
private BioSystem(Chunk chunk ,float[] nutrients)
{
this(chunk, nutrients[0], nutrients[1], nutrients[2]);
}
public BioSystem(Chunk chunk, float phosphorus, float potassium, float nitrogen)
{
this(chunk, 0F, 0F, phosphorus, potassium, nitrogen, 0F, 0F);
}
private BioSystem(Chunk chunk, float biomass, float nitrogenFixation, float phosphorus, float potassium, float nitrogen, float decomposingBacteria, float nitrifyingBacteria)
{
this.chunkReference = new WeakReference<Chunk>(chunk);
this.biomass = biomass;
this.nitrogenFixation = nitrogenFixation;
this.phosphorus = phosphorus;
this.potassium = potassium;
this.nitrogen = nitrogen;
this.decomposingBacteria = decomposingBacteria;
this.nitrifyingBacteria = nitrifyingBacteria;
}
public void addBiomass(float amount)
{
setChunkModified();
this.biomass += amount;
}
public void onGrowth(float bioMassIncrease, boolean addBiomass)
{
if (addBiomass)
addBiomass(bioMassIncrease);
phosphorus -= bioMassIncrease*Settings.BIOMASS_PHOSPHORUS_RATE;
potassium -= bioMassIncrease*Settings.BIOMASS_POTASSIUM_RATE;
nitrogen -= bioMassIncrease*Settings.BIOMASS_NITROGEN_RATE;
setChunkModified();
}
public void setBiomass(float amount)
{
setChunkModified();
biomass = amount;
setStableBacteriaValues();
}
public float getBiomass()
{
return biomass;
}
public float getNitrogenFixation()
{
return nitrogenFixation;
}
public void addNitrogenFixation(float amount)
{
setChunkModified();
this.nitrogenFixation += amount;
}
public float getPhosphorus()
{
return phosphorus;
}
public float getPotassium()
{
return potassium;
}
public float getNitrogen()
{
return nitrogen;
}
public float getDecomposingBacteria()
{
return decomposingBacteria;
}
public float getNitrifyingBacteria()
{
return nitrifyingBacteria;
}
public void setNitrogenFixation(float nitrogenFixation)
{
this.nitrogenFixation = nitrogenFixation;
}
public void setPhosphorus(float phosphorus)
{
this.phosphorus = phosphorus;
}
public void setPotassium(float potassium)
{
this.potassium = potassium;
}
public void setNitrogen(float nitrogen)
{
this.nitrogen = nitrogen;
}
public void setStableBacteriaValues()
{
decomposingBacteria = biomass + RANDOM.nextFloat()*(Settings.BACTERIA_GROWTH_MAX*biomass - biomass);
nitrifyingBacteria = biomass + RANDOM.nextFloat()*(Settings.BACTERIA_GROWTH_MAX*biomass - biomass);
}
public void setStableBacteriaValuesNearChunk()
{
setStableBacteriaValues();
Chunk chunk = chunkReference.get();
if (chunk != null)
{
getAndStabilizeChunk(chunk.worldObj, chunk.xPosition + 1, chunk.zPosition + 1);
getAndStabilizeChunk(chunk.worldObj, chunk.xPosition, chunk.zPosition + 1);
getAndStabilizeChunk(chunk.worldObj, chunk.xPosition + 1, chunk.zPosition);
getAndStabilizeChunk(chunk.worldObj, chunk.xPosition - 1, chunk.zPosition);
getAndStabilizeChunk(chunk.worldObj, chunk.xPosition, chunk.zPosition - 1);
getAndStabilizeChunk(chunk.worldObj, chunk.xPosition - 1, chunk.zPosition - 1);
getAndStabilizeChunk(chunk.worldObj, chunk.xPosition - 1, chunk.zPosition + 1);
getAndStabilizeChunk(chunk.worldObj, chunk.xPosition + 1, chunk.zPosition - 1);
}
}
public void getAndStabilizeChunk(World world, int x, int z)
{
if (world.chunkExists(x, z))
{
BioSystem bioSystem = BioSystemHandler.getBioSystem(world, world.getChunkFromChunkCoords(x, z));
if (bioSystem != null)
bioSystem.setStableBacteriaValues();
}
}
public void update()
{
tick++;
if (tick % Settings.TICKS_PER_BIOSYSTEM_UPDATE == 0)
{
setChunkModified();
Chunk chunk = chunkReference.get();
if (chunk != null)
{
if (chunk.xPosition == 0 && chunk.zPosition == 0)
{
System.out.println(String.format("PRE: Biomass: %f, Nitrogen Fixation: %f, Phosphorus: %f, Potassium: %f, Nitrogen %f, Decomposing Bacteria: %f, Nirtifying Bacteria %f", this.getBiomass(), this.getNitrogenFixation(), this.getPhosphorus(), this.getPotassium(), this.getNitrogen(), this.getDecomposingBacteria(), this.getNitrifyingBacteria()));
}
//TODO: BALANCE! BIOMASS INCREASE HAS A VERY DRAMATIC EFFECT, NUTRIENT USAGE TOO HIGH.
//Bacteria calculations
float biomassBacteriaRate = biomass / decomposingBacteria;
if (biomassBacteriaRate > Settings.BACTERIA_GROWTH_MAX)
{
decomposingBacteria += decomposingBacteria * Settings.BACTERIA_CHANGE_RATE;
}
else if (biomassBacteriaRate < Settings.BACTERIA_DEATH)
{
decomposingBacteria -= (1-biomassBacteriaRate)*decomposingBacteria * Settings.BACTERIA_CHANGE_RATE;
}
float nirtifyingBacteriaRate = (Math.min(biomass, decomposingBacteria) + nitrogenFixation) / nitrifyingBacteria;
if (nirtifyingBacteriaRate > Settings.BACTERIA_GROWTH_MAX)
{
nitrifyingBacteria += nitrifyingBacteria * Settings.BACTERIA_CHANGE_RATE;
}
else if (nirtifyingBacteriaRate < Settings.BACTERIA_DEATH)
{
nitrifyingBacteria -= (1-nirtifyingBacteriaRate)*nitrifyingBacteria * Settings.BACTERIA_CHANGE_RATE;
}
//Nutrient calculations
//TODO: figure out good change rates
phosphorus += Math.min(biomass, decomposingBacteria) * Settings.PHOSPHORUS_CHANGE_RATE;
phosphorus -= biomass * Settings.PHOSPHORUS_CHANGE_RATE;
phosphorus = Math.max(0, phosphorus);
potassium += Math.min(biomass, decomposingBacteria) * Settings.POTASSIUM_CHANGE_RATE;
potassium -= biomass * Settings.POTASSIUM_CHANGE_RATE;
potassium = Math.max(0, potassium);
//TODO: nitrogen fixation should be calculated diffently (should not be dependant on nitrogen change rate in plants)
nitrogen += Math.min(Math.min(biomass, decomposingBacteria) + nitrogenFixation, nitrifyingBacteria) * Settings.NITROGEN_CHANGE_RATE;
nitrogen -= biomass * Settings.NITROGEN_CHANGE_RATE;
nitrogen = Math.max(0, nitrogen);
//Spread BioSystem stuff to nearby chunks
spreadToChunk(chunk, chunk.xPosition - 1, chunk.zPosition);
spreadToChunk(chunk, chunk.xPosition + 1, chunk.zPosition);
spreadToChunk(chunk, chunk.xPosition, chunk.zPosition - 1);
spreadToChunk(chunk, chunk.xPosition, chunk.zPosition + 1);
if (chunk.xPosition == 0 && chunk.zPosition == 0)
{
System.out.println(String.format("SPREAD: Biomass: %f, Nitrogen Fixation: %f, Phosphorus: %f, Potassium: %f, Nitrogen %f, Decomposing Bacteria: %f, Nirtifying Bacteria %f", this.getBiomass(), this.getNitrogenFixation(), this.getPhosphorus(), this.getPotassium(), this.getNitrogen(), this.getDecomposingBacteria(), this.getNitrifyingBacteria()));
}
//Send the chunk biomass changes to all clients watching this chunk
NetworkHandler.INSTANCE.sendToPlayersWatchingChunk(new MessageBioSystemUpdate(this), (WorldServer) chunk.worldObj, chunk.xPosition, chunk.zPosition);
}
}
}
private void spreadToChunk(Chunk chunk, int xPos, int zPos)
{
if (chunk.worldObj.chunkExists(xPos, zPos))
{
Chunk chunk1 = chunk.worldObj.getChunkFromChunkCoords(xPos, zPos);
BioSystem bioSystem = BioSystemHandler.getBioSystem(chunk.worldObj, chunk1);
if (bioSystem != null)
spread(bioSystem);
else
Biota.logger.warn(String.format("Couldn't find BioSystem at: %d %d", xPos, zPos));
}
}
private void spread(BioSystem bioSystem)
{
float dP = this.phosphorus - bioSystem.phosphorus;
float dK = this.potassium - bioSystem.potassium;
float dN = this.nitrogen - bioSystem.nitrogen;
float dDB = this.decomposingBacteria - bioSystem.decomposingBacteria;
float dNB = this.nitrifyingBacteria - bioSystem.nitrifyingBacteria;
float spreadP = Settings.BIOSYSTEM_SPREAD_RATE *dP;
float spreadK = Settings.BIOSYSTEM_SPREAD_RATE *dK;
float spreadN = Settings.BIOSYSTEM_SPREAD_RATE *dN;
float spreadDB = Settings.BIOSYSTEM_SPREAD_RATE *dDB;
float spreadNB = Settings.BIOSYSTEM_SPREAD_RATE *dNB;
this.phosphorus -= spreadP;
bioSystem.phosphorus += spreadP;
this.potassium -= spreadK;
bioSystem.potassium += spreadK;
this.nitrogen -= spreadN;
bioSystem.nitrogen += spreadN;
this.decomposingBacteria -= spreadDB;
bioSystem.decomposingBacteria += spreadDB;
this.nitrifyingBacteria -= spreadNB;
bioSystem.nitrifyingBacteria += spreadNB;
}
public void setChunkModified()
{
Chunk chunk1 = chunkReference.get();
if (chunk1 != null)
{
chunk1.isModified = true;
}
}
public static BioSystem loadFromNBT(Chunk chunk, NBTTagCompound compound)
{
float biomass = compound.getFloat("biomass");
float nitrogenFixation = compound.getFloat("nitrogenFixation");
float phosphorus = compound.getFloat("phosphorus");
float potassium = compound.getFloat("potassium");
float nitrogen = compound.getFloat("nitrogen");
float decomposingBacteria = compound.getFloat("decomposingBacteria");
float nitrifyingBacteria = compound.getFloat("nitrifyingBacteria");
return new BioSystem(chunk, biomass, nitrogenFixation, phosphorus, potassium, nitrogen, decomposingBacteria, nitrifyingBacteria);
}
public void saveToNBT(NBTTagCompound compound)
{
compound.setFloat("biomass", biomass);
compound.setFloat("nitrogenFixation", nitrogenFixation);
compound.setFloat("phosphorus", phosphorus);
compound.setFloat("potassium", potassium);
compound.setFloat("nitrogen", nitrogen);
compound.setFloat("decomposingBacteria", decomposingBacteria);
compound.setFloat("nitrifyingBacteria", nitrifyingBacteria);
}
@Override
public String toString()
{
StringBuilder result = new StringBuilder();
String NEW_LINE = System.getProperty("line.separator");
result.append(this.getClass().getName());
result.append(" Object {");
result.append(NEW_LINE);
result.append(" biomass: ");
result.append(biomass);
result.append(NEW_LINE);
result.append(" nitrogen fixation: ");
result.append(nitrogenFixation);
result.append(NEW_LINE);
result.append(" phosphorus: ");
result.append(phosphorus);
result.append(NEW_LINE);
result.append(" potassium: ");
result.append(potassium);
result.append(NEW_LINE);
result.append(" nitrogen: ");
result.append(nitrogen);
result.append(NEW_LINE);
result.append(" decomposingBacteria: ");
result.append(decomposingBacteria);
result.append(NEW_LINE);
result.append(" nitrifyingBacteria: ");
result.append(nitrifyingBacteria);
result.append(NEW_LINE);
result.append("}");
return result.toString();
}
}