package com.bioxx.tfc2.world;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import com.bioxx.jmapgen.IslandMap;
import com.bioxx.jmapgen.IslandParameters.Feature;
import com.bioxx.libnoise.model.Line;
import com.bioxx.libnoise.model.Plane;
import com.bioxx.libnoise.module.combiner.Max;
import com.bioxx.libnoise.module.combiner.Select;
import com.bioxx.libnoise.module.modifier.Clamp;
import com.bioxx.libnoise.module.modifier.TranslatePoint;
import com.bioxx.libnoise.module.source.Const;
import com.bioxx.libnoise.module.source.Perlin;
import com.bioxx.tfc2.Core;
import com.bioxx.tfc2.api.types.ClimateTemp;
import com.bioxx.tfc2.api.types.Moisture;
import com.bioxx.tfc2.api.types.Season;
import com.bioxx.tfc2.api.types.SeasonalPeriod;
import com.bioxx.tfc2.core.Timekeeper;
/**
* This class acts as the overall weather manager for all islands. A perlin map
* @author Bioxx
*
*/
public class WeatherManager
{
private static WeatherManager instance;
private static final float[] hourlyTempMod = new float[] {0.90f/*12am*/, 0.88f, 0.86f, 0.84f, 0.82f, 0.8f, 0.81f/*6am*/,
0.82f, 0.84f, 0.86f, 0.88f, 0.9f, 0.92f/*12pm*/, 0.94f, 0.96f, 0.98f, 1.0f, 0.99f, 0.98f/*6pm*/, 0.97f, 0.96f, 0.95f, 0.94f, 0.92f};
private int rainCounter = 0;
public Line rainModelSpring;
public Line rainModelSummer;
public Line rainModelFall;
public Line rainModelWinter;
public Line rainModelDesert;
private Plane temperatureNoise;
private World worldObj;
public WeatherManager(World world)
{
worldObj = world;
}
public static void setupWeather(World world)
{
instance = new WeatherManager(world);
instance.setupStorms(world);
}
public static WeatherManager getInstance()
{
return instance;
}
/**
* @param model Rain Model
* @param x Island Map XCoord
* @param z Island Map ZCoord
* @return Returns precipitation value from the perlin map
*/
public double getPrecipitationRaw(Line model, int x, int z)
{
//We use the x value for moving the noise over time
//The y value is used as an offset for the island x coordinate
//The z value is used as an offset for the island z coordinate
return model.getValue(Timekeeper.getInstance().getTotalHalfHours(), x * 1000000, z * 1000000);
}
/**
* @return Returns an adjusted precipitation value for the island being queried
*/
public double getPreciptitation(IslandMap island, int x, int z)
{
Line model = getModelForClimate(island, worldObj);
double raw = getPrecipitationRaw(model, x >> 12, z >> 12);
//We divide the clamp by two so that there is more rain on average. Just using the clamp itself was too limiting
double clamp = island.getParams().getIslandMoisture().getInverse() / 2D;
double rain = Math.max(Math.min(raw - clamp, 1.0), 0.0);
return rain;
}
public double getPreciptitation(int x, int z)
{
IslandMap island = WorldGen.getInstance().getIslandMap(x >> 12, z >> 12);
return getPreciptitation(island, x, z);
}
public Line getModelForClimate(IslandMap map, World world)
{
Moisture moisture = map.getParams().getIslandMoisture();
ClimateTemp temp = map.getParams().getIslandTemp();
Season season = Timekeeper.getInstance().getSeason();
if(map.getParams().hasFeature(Feature.Desert))
return rainModelDesert;
//TODO: Expand this to allow islands to have more individualized weather
if(season == Season.Spring)
return rainModelSpring;
else if(season == Season.Summer)
return rainModelSummer;
else if(season == Season.Fall)
return rainModelFall;
else if(season == Season.Winter)
return rainModelWinter;
return rainModelSummer;
}
public double getTemperature(BlockPos pos)
{
IslandMap island = WorldGen.getInstance().getIslandMap(pos.getX() >> 12, pos.getZ() >> 12);
return getTemperature(island, pos);
}
public double getTemperature(IslandMap island, BlockPos pos)
{
Timekeeper inst = Timekeeper.getInstance();
// 1: Find the island information to get the general climate data
ClimateTemp climate = island.getParams().getIslandTemp();
// 2: Get seasonal data to combine with the island information
SeasonalPeriod period = Timekeeper.getInstance().getSeasonalPeriod();
// 3: Get local temperature noise
double local = temperatureNoise.getModule().GetValue(pos.getX(), Timekeeper.getInstance().getTotalDays(), pos.getZ()) * climate.getTempVar();
// 4: Combine this information and adjust for elevation;
double baseMin = climate.getTempMin();
double baseMax = climate.getTempMax();
double elevationModifier = (((pos.getY()-64)/192D)*8.0);
return (baseMin + ((baseMax - baseMin)*period.getTempMultiplier()) + local) * hourlyTempMod[Timekeeper.getInstance().getClockTime()] - elevationModifier;
}
public double getTemperature(int x, int y, int z)
{
return getTemperature(Core.getMapForWorld(worldObj, new BlockPos(x, y, z)), new BlockPos(x, y, z));
}
public void setupStorms(World world)
{
rainModelSummer = getSummerStorm();
rainModelSpring = getSpringStorm();
rainModelFall = getFallStorm();
rainModelWinter = getWinterStorm();
Perlin p0 = new Perlin(worldObj.getSeed(), 0.003, 0.8);
p0.setOctaveCount(4);
p0.setLacunarity(1.1);
this.temperatureNoise = new Plane(p0);
Const c0 = new Const();
rainModelDesert = new Line(c0);
}
private Line getSummerStorm()
{
/*
* Rain band module. High frequency causes the rain strength to fluctuate a lot.
*/
Perlin p0 = new Perlin(worldObj.getSeed(), 0.05, 0.8);
p0.setOctaveCount(2);
p0.setLacunarity(1.1);
/*
* Used by the Selector for dry spells
*/
Const con0 = new Const(0);
/*
* Control module - This creates the Large Storms
*/
Perlin p1 = new Perlin(worldObj.getSeed(), 0.008, 0.1);
/**
* Select module uses the control module to determine how often it will be rainy over the course of the
* season, then fills the rainy periods with rain bands from p0
*/
Select s0 = new Select(0.1, 1.0, 0.5, p1, con0, p0);
/**
* The default storms are nice but its hard to get the amount of rain bands that we'd like
* to see so we translate the initial rain map to get some new noise and then blend it into our final map
*/
TranslatePoint tp0 = new TranslatePoint();
tp0.setSourceModule(0, p0);
tp0.setZTranslation(1000);
/**
* This is the selection module for our second rain band pass
*/
Select s1 = new Select(0.1, 1.0, 0.5, p1, con0, tp0);
/**
* We use max to blend the rain band layers.
*/
Max m0 = new Max();
m0.setSourceModule(0, s0);
m0.setSourceModule(1, s1);
/*
* Clamps our output to 0.0 - 1.0
*/
Clamp c0 = new Clamp(m0);
return new Line(c0);
}
private Line getSpringStorm()
{
/*
* Rain band module. High frequency causes the rain strength to fluctuate a lot.
*/
Perlin p0 = new Perlin(worldObj.getSeed(), 0.1, 0.8);
p0.setOctaveCount(2);
p0.setLacunarity(1.1);
/*
* Used by the Selector for dry spells
*/
Const con0 = new Const(0);
/*
* Control module - This creates the Large Storms
*/
Perlin p1 = new Perlin(worldObj.getSeed(), 0.019, 0.1);
/**
* Select module uses the control module to determine how often it will be rainy over the course of the
* season, then fills the rainy periods with rain bands from p0
*/
Select s0 = new Select(0.1, 1.0, 0.5, p1, con0, p0);
/**
* The default storms are nice but its hard to get the amount of rain bands that we'd like
* to see so we translate the initial rain map to get some new noise and then blend it into our final map
*/
TranslatePoint tp0 = new TranslatePoint();
tp0.setSourceModule(0, p0);
tp0.setZTranslation(1000);
/**
* This is the selection module for our second rain band pass
*/
Select s1 = new Select(0.1, 1.0, 0.5, p1, con0, tp0);
/**
* We use max to blend the rain band layers.
*/
Max m0 = new Max();
m0.setSourceModule(0, s0);
m0.setSourceModule(1, s1);
/*
* Clamps our output to 0.0 - 1.0
*/
Clamp c0 = new Clamp(m0);
return new Line(c0);
}
private Line getFallStorm()
{
/*
* Rain band module. High frequency causes the rain strength to fluctuate a lot.
*/
Perlin p0 = new Perlin(worldObj.getSeed(), 0.05, 0.8);
p0.setOctaveCount(2);
p0.setLacunarity(1.1);
/*
* Used by the Selector for dry spells
*/
Const con0 = new Const(0);
/*
* Control module - This creates the Large Storms
*/
Perlin p1 = new Perlin(worldObj.getSeed(), 0.008, 0.1);
/**
* Select module uses the control module to determine how often it will be rainy over the course of the
* season, then fills the rainy periods with rain bands from p0
*/
Select s0 = new Select(0.1, 1.0, 0.5, p1, con0, p0);
/**
* The default storms are nice but its hard to get the amount of rain bands that we'd like
* to see so we translate the initial rain map to get some new noise and then blend it into our final map
*/
TranslatePoint tp0 = new TranslatePoint();
tp0.setSourceModule(0, p0);
tp0.setZTranslation(1000);
/**
* This is the selection module for our second rain band pass
*/
Select s1 = new Select(0.1, 1.0, 0.5, p1, con0, tp0);
/**
* We use max to blend the rain band layers.
*/
Max m0 = new Max();
m0.setSourceModule(0, s0);
m0.setSourceModule(1, s1);
/*
* Clamps our output to 0.0 - 1.0
*/
Clamp c0 = new Clamp(m0);
return new Line(c0);
}
private Line getWinterStorm()
{
/*
* Rain band module. High frequency causes the rain strength to fluctuate a lot.
*/
Perlin p0 = new Perlin(worldObj.getSeed(), 0.05, 0.8);
p0.setOctaveCount(2);
p0.setLacunarity(1.1);
/*
* Used by the Selector for dry spells
*/
Const con0 = new Const(0);
/*
* Control module - This creates the Large Storms
*/
Perlin p1 = new Perlin(worldObj.getSeed(), 0.008, 0.1);
/**
* Select module uses the control module to determine how often it will be rainy over the course of the
* season, then fills the rainy periods with rain bands from p0
*/
Select s0 = new Select(0.1, 1.0, 0.5, p1, con0, p0);
/**
* The default storms are nice but its hard to get the amount of rain bands that we'd like
* to see so we translate the initial rain map to get some new noise and then blend it into our final map
*/
TranslatePoint tp0 = new TranslatePoint();
tp0.setSourceModule(0, p0);
tp0.setZTranslation(1000);
/**
* This is the selection module for our second rain band pass
*/
Select s1 = new Select(0.1, 1.0, 0.5, p1, con0, tp0);
/**
* We use max to blend the rain band layers.
*/
Max m0 = new Max();
m0.setSourceModule(0, s0);
m0.setSourceModule(1, s1);
/*
* Clamps our output to 0.0 - 1.0
*/
Clamp c0 = new Clamp(m0);
return new Line(c0);
}
}