package net.minecraftforge.common;
import java.util.*;
import cpw.mods.fml.common.FMLLog;
import net.minecraft.init.Blocks;
import net.minecraft.world.biome.*;
import net.minecraftforge.common.util.EnumHelper;
import net.minecraftforge.event.terraingen.DeferredBiomeDecorator;
import static net.minecraft.world.biome.BiomeGenBase.*;
import static net.minecraftforge.common.BiomeDictionary.Type.*;
public class BiomeDictionary
{
public enum Type
{
/*Temperature-based tags. Specifying neither implies a biome is temperate*/
HOT,
COLD,
/*Tags specifying the amount of vegetation a biome has. Specifying neither implies a biome to have moderate amounts*/
SPARSE,
DENSE,
/*Tags specifying how moist a biome is. Specifying neither implies the biome as having moderate humidity*/
WET,
DRY,
/*Tree-based tags, SAVANNA refers to dry, desert-like trees (Such as Acacia), CONIFEROUS refers to snowy trees (Such as Spruce) and JUNGLE refers to jungle trees.
* Specifying no tag implies a biome has temperate trees (Such as Oak)*/
SAVANNA,
CONIFEROUS,
JUNGLE,
/*Tags specifying the nature of a biome*/
SPOOKY,
DEAD,
LUSH,
NETHER,
END,
MUSHROOM,
MAGICAL,
OCEAN,
RIVER,
/**A general tag for all water-based biomes. Shown as present if OCEAN or RIVER are.**/
WATER(OCEAN, RIVER),
/*Generic types which a biome can be*/
MESA,
FOREST,
PLAINS,
MOUNTAIN,
HILLS,
SWAMP,
SANDY,
SNOWY,
WASTELAND,
BEACH,
/*Deprecated tags, kept for compatibility*/
@Deprecated
/**Replaced by SANDY**/
DESERT(SANDY),
@Deprecated
/**Replaced by SNOWY**/
FROZEN(SNOWY);
private List<Type> subTags;
private Type(Type... subTags)
{
this.subTags = Arrays.asList(subTags);
}
private boolean hasSubTags()
{
return subTags != null && !subTags.isEmpty();
}
/**
* Retrieves a Type value by name,
* if one does not exist already it creates one.
* This can be used as interm measure for modders to
* add there own category of Biome.
*
* There are NO naming conventions besides:
* MUST be all upper case (enforced by name.toUpper())
* NO Special characters. {Unenforced, just don't be a pain, if it becomes a issue I WILL
* make this RTE with no worry about backwards compatibility}
*
* Note: For performance sake, the return value of this function SHOULD be cached.
* Two calls with the same name SHOULD return the same value.
*
*
* @param name The name of this Type
* @return An instance of Type for this name.
*/
public static Type getType(String name, Type... subTypes)
{
name = name.toUpperCase();
for (Type t : values())
{
if (t.name().equals(name))
return t;
}
Type ret = EnumHelper.addEnum(Type.class, name, new Class[]{Type[].class}, new Object[]{subTypes});
if (ret.ordinal() >= typeInfoList.length)
{
typeInfoList = Arrays.copyOf(typeInfoList, ret.ordinal());
}
return ret;
}
}
private static final int BIOME_LIST_SIZE = BiomeGenBase.getBiomeGenArray().length;
private static BiomeInfo[] biomeList = new BiomeInfo[BIOME_LIST_SIZE];
@SuppressWarnings("unchecked")
private static ArrayList<BiomeGenBase>[] typeInfoList = new ArrayList[Type.values().length];
private static class BiomeInfo
{
public EnumSet<Type> typeList;
public BiomeInfo(Type[] types)
{
typeList = EnumSet.noneOf(Type.class);
for(Type t : types)
{
typeList.add(t);
}
}
}
static
{
registerVanillaBiomes();
}
/**
* Registers a biome with a specific biome type
*
* @param biome the biome to be registered
* @param type the type to register the biome as
* @return returns true if the biome was registered successfully
*/
public static boolean registerBiomeType(BiomeGenBase biome, Type ... types)
{
types = listSubTags(types);
if(BiomeGenBase.getBiomeGenArray()[biome.biomeID] != null)
{
for(Type type : types)
{
if(typeInfoList[type.ordinal()] == null)
{
typeInfoList[type.ordinal()] = new ArrayList<BiomeGenBase>();
}
typeInfoList[type.ordinal()].add(biome);
}
if(biomeList[biome.biomeID] == null)
{
biomeList[biome.biomeID] = new BiomeInfo(types);
}
else
{
for(Type type : types)
{
biomeList[biome.biomeID].typeList.add(type);
}
}
return true;
}
return false;
}
/**
* Returns a list of biomes registered with a specific type
*
* @param type the Type to look for
* @return a list of biomes of the specified type, null if there are none
*/
public static BiomeGenBase[] getBiomesForType(Type type)
{
if(typeInfoList[type.ordinal()] != null)
{
return (BiomeGenBase[])typeInfoList[type.ordinal()].toArray(new BiomeGenBase[0]);
}
return new BiomeGenBase[0];
}
/**
* Gets a list of Types that a specific biome is registered with
*
* @param biome the biome to check
* @return the list of types, null if there are none
*/
public static Type[] getTypesForBiome(BiomeGenBase biome)
{
checkRegistration(biome);
if(biomeList[biome.biomeID] != null)
{
return (Type[])biomeList[biome.biomeID].typeList.toArray(new Type[0]);
}
return new Type[0];
}
/**
* Checks to see if two biomes are registered as having the same type
*
* @param biomeA
* @param biomeB
* @return returns true if a common type is found, false otherwise
*/
public static boolean areBiomesEquivalent(BiomeGenBase biomeA, BiomeGenBase biomeB)
{
int a = biomeA.biomeID;
int b = biomeB.biomeID;
checkRegistration(biomeA);
checkRegistration(biomeB);
if(biomeList[a] != null && biomeList[b] != null)
{
for(Type type : biomeList[a].typeList)
{
if(containsType(biomeList[b], type))
{
return true;
}
}
}
return false;
}
/**
* Checks to see if the given biome is registered as being a specific type
*
* @param biome the biome to be considered
* @param type the type to check for
* @return returns true if the biome is registered as being of type type, false otherwise
*/
public static boolean isBiomeOfType(BiomeGenBase biome, Type type)
{
checkRegistration(biome);
if(biomeList[biome.biomeID] != null)
{
return containsType(biomeList[biome.biomeID], type);
}
return false;
}
/**
* Checks to see if the given biome has been registered as being of any type
* @param biome the biome to consider
* @return returns true if the biome has been registered, false otherwise
*/
public static boolean isBiomeRegistered(BiomeGenBase biome)
{
return biomeList[biome.biomeID] != null;
}
public static boolean isBiomeRegistered(int biomeID)
{
return biomeList[biomeID] != null;
}
public static void registerAllBiomes()
{
FMLLog.warning("Redundant call to BiomeDictionary.registerAllBiomes ignored");
}
/**
* Loops through the biome list and automatically adds tags to any biome that does not have any
* This is called by Forge at postinit time. It will additionally dispatch any deferred decorator
* creation events.
*
* DO NOT call this during world generation
*/
public static void registerAllBiomesAndGenerateEvents()
{
for(int i = 0; i < BiomeGenBase.getBiomeGenArray().length; i++)
{
BiomeGenBase biome = BiomeGenBase.getBiomeGenArray()[i];
if(biome == null)
{
continue;
}
if (biome.theBiomeDecorator instanceof DeferredBiomeDecorator)
{
DeferredBiomeDecorator decorator = (DeferredBiomeDecorator) biome.theBiomeDecorator;
decorator.fireCreateEventAndReplace(biome);
}
checkRegistration(biome);
}
}
/**
* Automatically looks for and registers a given biome with appropriate tags
* This method is called automatically if a biome has not been registered with any tags,
* And another method requests information about it
*
* @param biome the biome to be considered
*/
public static void makeBestGuess(BiomeGenBase biome)
{
if (biome.theBiomeDecorator.treesPerChunk >= 3)
{
if (biome.isHighHumidity() && biome.temperature >= 0.9F)
{
BiomeDictionary.registerBiomeType(biome, JUNGLE);
}
else if (!biome.isHighHumidity())
{
BiomeDictionary.registerBiomeType(biome, FOREST);
if (biome.temperature <= 0.2f)
{
BiomeDictionary.registerBiomeType(biome, CONIFEROUS);
}
}
}
else if(biome.heightVariation <= 0.3F && biome.heightVariation >= 0.0F)
{
if(!biome.isHighHumidity() || biome.rootHeight >= 0.0F)
{
BiomeDictionary.registerBiomeType(biome, PLAINS);
}
}
if (biome.rainfall > 0.85f)
{
BiomeDictionary.registerBiomeType(biome, WET);
}
if (biome.rainfall < 0.15f)
{
BiomeDictionary.registerBiomeType(biome, DRY);
}
if (biome.temperature > 0.85f)
{
BiomeDictionary.registerBiomeType(biome, HOT);
}
if (biome.temperature < 0.15f)
{
BiomeDictionary.registerBiomeType(biome, COLD);
}
if (biome.theBiomeDecorator.treesPerChunk > 0 && biome.theBiomeDecorator.treesPerChunk < 3)
{
BiomeDictionary.registerBiomeType(biome, SPARSE);
}
else if (biome.theBiomeDecorator.treesPerChunk >= 10)
{
BiomeDictionary.registerBiomeType(biome, DENSE);
}
if (biome.isHighHumidity() && biome.rootHeight < 0.0F && (biome.heightVariation <= 0.3F && biome.heightVariation >= 0.0F))
{
BiomeDictionary.registerBiomeType(biome, SWAMP);
}
if (biome.rootHeight <= -0.5F)
{
if (biome.heightVariation == 0.0F)
{
BiomeDictionary.registerBiomeType(biome, RIVER);
}
else
{
BiomeDictionary.registerBiomeType(biome, OCEAN);
}
}
if (biome.heightVariation >= 0.4F && biome.heightVariation < 1.5F)
{
BiomeDictionary.registerBiomeType(biome, HILLS);
}
if (biome.heightVariation >= 1.5F)
{
BiomeDictionary.registerBiomeType(biome, MOUNTAIN);
}
if (biome.getEnableSnow())
{
BiomeDictionary.registerBiomeType(biome, SNOWY);
}
if (biome.topBlock != Blocks.sand && biome.temperature >= 1.0f && biome.rainfall < 0.2f)
{
BiomeDictionary.registerBiomeType(biome, SAVANNA);
}
if (biome.topBlock == Blocks.sand )
{
BiomeDictionary.registerBiomeType(biome, SANDY);
}
else if (biome.topBlock == Blocks.hardened_clay)
{
BiomeDictionary.registerBiomeType(biome, MESA);
}
else if (biome.topBlock == Blocks.mycelium)
{
BiomeDictionary.registerBiomeType(biome, MUSHROOM);
}
}
//Internal implementation
private static void checkRegistration(BiomeGenBase biome)
{
if(!isBiomeRegistered(biome))
{
makeBestGuess(biome);
}
}
private static boolean containsType(BiomeInfo info, Type type)
{
if (type.hasSubTags())
{
for (Type remappedType : listSubTags(type))
{
if (info.typeList.contains(remappedType)) return true;
}
return false;
}
return info.typeList.contains(type);
}
private static Type[] listSubTags(Type... types)
{
List<Type> subTags = new ArrayList<Type>();
for (Type type : types)
{
if (type.hasSubTags()) subTags.addAll(type.subTags);
else subTags.add(type);
}
return subTags.toArray(new Type[subTags.size()]);
}
private static void registerVanillaBiomes()
{
registerBiomeType(ocean, OCEAN );
registerBiomeType(plains, PLAINS );
registerBiomeType(desert, HOT, DRY, SANDY );
registerBiomeType(extremeHills, MOUNTAIN, HILLS );
registerBiomeType(forest, FOREST );
registerBiomeType(taiga, COLD, CONIFEROUS, FOREST );
registerBiomeType(taigaHills, COLD, CONIFEROUS, FOREST, HILLS );
registerBiomeType(swampland, WET, SWAMP );
registerBiomeType(river, RIVER );
registerBiomeType(frozenOcean, COLD, OCEAN, SNOWY );
registerBiomeType(frozenRiver, COLD, RIVER, SNOWY );
registerBiomeType(icePlains, COLD, SNOWY, WASTELAND );
registerBiomeType(iceMountains, COLD, SNOWY, MOUNTAIN );
registerBiomeType(beach, BEACH );
registerBiomeType(desertHills, HOT, DRY, SANDY, HILLS );
registerBiomeType(jungle, HOT, WET, DENSE, JUNGLE );
registerBiomeType(jungleHills, HOT, WET, DENSE, JUNGLE, HILLS);
registerBiomeType(forestHills, FOREST, HILLS );
registerBiomeType(sky, COLD, DRY, END );
registerBiomeType(hell, HOT, DRY, NETHER );
registerBiomeType(mushroomIsland, MUSHROOM );
registerBiomeType(extremeHillsEdge, MOUNTAIN );
registerBiomeType(mushroomIslandShore, MUSHROOM, BEACH );
registerBiomeType(jungleEdge, HOT, WET, JUNGLE, FOREST );
registerBiomeType(deepOcean, OCEAN );
registerBiomeType(stoneBeach, BEACH );
registerBiomeType(coldBeach, COLD, BEACH, SNOWY );
registerBiomeType(birchForest, FOREST );
registerBiomeType(birchForestHills, FOREST, HILLS );
registerBiomeType(roofedForest, SPOOKY, DENSE, FOREST );
registerBiomeType(coldTaiga, COLD, CONIFEROUS, FOREST, SNOWY );
registerBiomeType(coldTaigaHills, COLD, CONIFEROUS, FOREST, SNOWY, HILLS);
registerBiomeType(megaTaiga, COLD, CONIFEROUS, FOREST );
registerBiomeType(megaTaigaHills, COLD, CONIFEROUS, FOREST, HILLS );
registerBiomeType(extremeHillsPlus, MOUNTAIN, FOREST, SPARSE );
registerBiomeType(savanna, HOT, SAVANNA, PLAINS, SPARSE );
registerBiomeType(savannaPlateau, HOT, SAVANNA, PLAINS, SPARSE );
registerBiomeType(mesa, MESA, SANDY );
registerBiomeType(mesaPlateau_F, MESA, SPARSE, SANDY );
registerBiomeType(mesaPlateau, MESA, SANDY );
}
}