package blusunrize.immersiveengineering.api.tool;
import blusunrize.immersiveengineering.ImmersiveEngineering;
import blusunrize.immersiveengineering.api.ApiUtils;
import blusunrize.immersiveengineering.api.DimensionChunkCoords;
import blusunrize.immersiveengineering.api.IEApi;
import blusunrize.immersiveengineering.common.IESaveData;
import blusunrize.immersiveengineering.common.util.network.MessageMineralListSync;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagFloat;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.nbt.NBTTagString;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.relauncher.Side;
import java.util.*;
/**
* @author BluSunrize - 03.06.2015
*
* The Handler for the Excavator. Chunk->Ore calculation is done here, as is registration
*/
public class ExcavatorHandler
{
/**
* A HashMap of MineralMixes and their rarity (Integer out of 100)
*/
public static LinkedHashMap<MineralMix, Integer> mineralList = new LinkedHashMap<MineralMix, Integer>();
public static HashMap<DimensionChunkCoords, MineralWorldInfo> mineralCache = new HashMap<DimensionChunkCoords, MineralWorldInfo>();
private static HashMap<Integer,Integer> dimensionBasedTotalWeight = new HashMap<Integer,Integer>();
public static int mineralVeinCapacity = 0;
public static double mineralChance = 0;
public static int[] defaultDimensionBlacklist = new int[0];
public static boolean allowPackets = false;
public static MineralMix addMineral(String name, int mineralWeight, float failChance, String[] ores, float[] chances)
{
assert ores.length == chances.length;
MineralMix mix = new MineralMix(name, failChance, ores, chances);
mineralList.put(mix, mineralWeight);
return mix;
}
public static void recalculateChances(boolean mutePackets)
{
for(Map.Entry<MineralMix, Integer> e : mineralList.entrySet())
e.getKey().recalculateChances();
dimensionBasedTotalWeight.clear();
if(FMLCommonHandler.instance().getEffectiveSide()==Side.SERVER && allowPackets && !mutePackets)
{
HashMap<MineralMix,Integer> packetMap = new HashMap<MineralMix,Integer>();
for(Map.Entry<MineralMix,Integer> e: ExcavatorHandler.mineralList.entrySet())
if(e.getKey()!=null && e.getValue()!=null)
packetMap.put(e.getKey(), e.getValue());
ImmersiveEngineering.packetHandler.sendToAll(new MessageMineralListSync(packetMap));
}
}
public static int getDimensionTotalWeight(int dim)
{
if(dimensionBasedTotalWeight.containsKey(dim))
return dimensionBasedTotalWeight.get(dim);
int totalWeight = 0;
for(Map.Entry<MineralMix, Integer> e : mineralList.entrySet())
{
e.getKey().recalculateChances();
if(e.getKey().isValid() && e.getKey().validDimension(dim))
totalWeight += e.getValue();
}
dimensionBasedTotalWeight.put(dim,totalWeight);
return totalWeight;
}
public static MineralMix getRandomMineral(World world, int chunkX, int chunkZ)
{
if(world.isRemote)
return null;
MineralWorldInfo info = getMineralWorldInfo(world,chunkX,chunkZ);
if(info==null || (info.mineral==null && info.mineralOverride==null))
return null;
if(mineralVeinCapacity>=0 && info.depletion>mineralVeinCapacity)
return null;
return info.mineralOverride!=null?info.mineralOverride:info.mineral;
}
public static MineralWorldInfo getMineralWorldInfo(World world, int chunkX, int chunkZ)
{
if(world.isRemote)
return null;
int dim = world.provider.getDimension();
DimensionChunkCoords coords = new DimensionChunkCoords(dim, chunkX,chunkZ);
MineralWorldInfo worldInfo = mineralCache.get(coords);
if(worldInfo==null)
{
MineralMix mix = null;
Random r = world.getChunkFromChunkCoords(chunkX, chunkZ).getRandomWithSeed(940610);
double dd = r.nextDouble();
boolean empty = dd>mineralChance;
int query = r.nextInt();
if(!empty)
{
int weight = Math.abs(query%getDimensionTotalWeight(dim));
for(Map.Entry<MineralMix, Integer> e : mineralList.entrySet())
if(e.getKey().isValid()&&e.getKey().validDimension(dim))
{
weight -= e.getValue();
if(weight < 0)
{
mix = e.getKey();
break;
}
}
}
worldInfo = new MineralWorldInfo();
worldInfo.mineral = mix;
mineralCache.put(coords, worldInfo);
}
return worldInfo;
}
public static void depleteMinerals(World world, int chunkX, int chunkZ)
{
MineralWorldInfo info = getMineralWorldInfo(world,chunkX,chunkZ);
info.depletion++;
IESaveData.setDirty(world.provider.getDimension());
}
public static class MineralMix
{
public String name;
public float failChance;
public String[] ores;
public float[] chances;
public ItemStack[] oreOutput;
public float[] recalculatedChances;
boolean isValid = false;
/**Should an ore given to this mix not be present in the dictionary, it will attempt to draw a replacement from this list*/
public HashMap<String,String> replacementOres;
public int[] dimensionWhitelist = new int[0];
public int[] dimensionBlacklist = new int[0];
public MineralMix(String name, float failChance, String[] ores, float[] chances)
{
this.name = name;
this.failChance = failChance;
this.ores = ores;
this.chances = chances;
this.dimensionBlacklist = defaultDimensionBlacklist.clone();
}
public MineralMix addReplacement(String original, String replacement)
{
if(replacementOres==null)
replacementOres = new HashMap();
replacementOres.put(original, replacement);
return this;
}
public void recalculateChances()
{
double chanceSum = 0;
ArrayList<ItemStack> existing = new ArrayList();
ArrayList<Double> reChances = new ArrayList();
for(int i=0; i<ores.length; i++)
{
String ore = ores[i];
if(replacementOres!=null && !ApiUtils.isExistingOreName(ore) && replacementOres.containsKey(ore))
ore = replacementOres.get(ore);
if(ore!=null && !ore.isEmpty() && ApiUtils.isExistingOreName(ore))
{
ItemStack preferredOre = IEApi.getPreferredOreStack(ore);
if(preferredOre!=null)
{
existing.add(preferredOre);
reChances.add((double)chances[i]);
chanceSum += chances[i];
}
}
}
isValid = existing.size()>0;
oreOutput = existing.toArray(new ItemStack[existing.size()]);
recalculatedChances = new float[reChances.size()];
for(int i=0; i<reChances.size(); i++)
recalculatedChances[i] = (float)(reChances.get(i)/chanceSum);
}
public ItemStack getRandomOre(Random rand)
{
float r = rand.nextFloat();
for(int i=0; i<recalculatedChances.length; i++)
{
r -= recalculatedChances[i];
if(r < 0)
return this.oreOutput[i];
}
return null;
}
public boolean isValid()
{
return isValid;
}
public boolean validDimension(int dim)
{
if(dimensionWhitelist!=null&&dimensionWhitelist.length>0)
{
for(int white : dimensionWhitelist)
if(dim==white)
return true;
return false;
}
else if(dimensionBlacklist!=null&&dimensionBlacklist.length>0)
{
for(int black : dimensionBlacklist)
if(dim==black)
return false;
return true;
}
return true;
}
public NBTTagCompound writeToNBT()
{
NBTTagCompound tag = new NBTTagCompound();
tag.setString("name", this.name);
tag.setFloat("failChance", this.failChance);
NBTTagList tagList = new NBTTagList();
for(String ore : this.ores)
tagList.appendTag(new NBTTagString(ore));
tag.setTag("ores", tagList);
tagList = new NBTTagList();
for(float chance : this.chances)
tagList.appendTag(new NBTTagFloat(chance));
tag.setTag("chances", tagList);
tagList = new NBTTagList();
if(this.oreOutput!=null)
for(ItemStack output : this.oreOutput)
tagList.appendTag(output.writeToNBT(new NBTTagCompound()));
tag.setTag("oreOutput", tagList);
tagList = new NBTTagList();
for(float chance : this.recalculatedChances)
tagList.appendTag(new NBTTagFloat(chance));
tag.setTag("recalculatedChances", tagList);
tag.setBoolean("isValid", isValid);
tag.setIntArray("dimensionWhitelist", dimensionWhitelist);
tag.setIntArray("dimensionBlacklist", dimensionBlacklist);
return tag;
}
public static MineralMix readFromNBT(NBTTagCompound tag)
{
String name = tag.getString("name");
float failChance = tag.getFloat("failChance");
NBTTagList tagList = tag.getTagList("ores", 8);
String[] ores = new String[tagList.tagCount()];
for(int i=0; i<ores.length; i++)
ores[i] = tagList.getStringTagAt(i);
tagList = tag.getTagList("chances", 5);
float[] chances = new float[tagList.tagCount()];
for(int i=0; i<chances.length; i++)
chances[i] = tagList.getFloatAt(i);
tagList = tag.getTagList("oreOutput", 10);
ItemStack[] oreOutput = new ItemStack[tagList.tagCount()];
for(int i=0; i<oreOutput.length; i++)
oreOutput[i] = ItemStack.loadItemStackFromNBT(tagList.getCompoundTagAt(i));
tagList = tag.getTagList("recalculatedChances", 5);
float[] recalculatedChances = new float[tagList.tagCount()];
for(int i=0; i<recalculatedChances.length; i++)
recalculatedChances[i] = tagList.getFloatAt(i);
boolean isValid = tag.getBoolean("isValid");
MineralMix mix = new MineralMix(name, failChance, ores, chances);
mix.oreOutput = oreOutput;
mix.recalculatedChances = recalculatedChances;
mix.isValid = isValid;
mix.dimensionWhitelist = tag.getIntArray("dimensionWhitelist");
mix.dimensionBlacklist = tag.getIntArray("dimensionBlacklist");
return mix;
}
}
public static class MineralWorldInfo
{
public MineralMix mineral;
public MineralMix mineralOverride;
public int depletion;
public NBTTagCompound writeToNBT()
{
NBTTagCompound tag = new NBTTagCompound();
if(mineral!=null)
tag.setString("mineral", mineral.name);
if(mineralOverride!=null)
tag.setString("mineralOverride", mineralOverride.name);
tag.setInteger("depletion", depletion);
return tag;
}
public static MineralWorldInfo readFromNBT(NBTTagCompound tag)
{
MineralWorldInfo info = new MineralWorldInfo();
if(tag.hasKey("mineral"))
{
String s = tag.getString("mineral");
for(MineralMix mineral : mineralList.keySet())
if(s.equalsIgnoreCase(mineral.name))
info.mineral = mineral;
}
if(tag.hasKey("mineralOverride"))
{
String s = tag.getString("mineralOverride");
for(MineralMix mineral : mineralList.keySet())
if(s.equalsIgnoreCase(mineral.name))
info.mineralOverride = mineral;
}
info.depletion = tag.getInteger("depletion");
return info;
}
}
}