package rtg.event;
import java.util.ArrayList;
import java.util.WeakHashMap;
import net.minecraft.block.BlockSapling;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.biome.Biome;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.terraingen.DecorateBiomeEvent;
import net.minecraftforge.event.terraingen.OreGenEvent;
import net.minecraftforge.event.terraingen.SaplingGrowTreeEvent;
import net.minecraftforge.event.terraingen.WorldTypeEvent;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import static net.minecraftforge.event.terraingen.DecorateBiomeEvent.Decorate.EventType.LAKE_LAVA;
import static net.minecraftforge.event.terraingen.DecorateBiomeEvent.Decorate.EventType.LAKE_WATER;
import static net.minecraftforge.event.terraingen.OreGenEvent.GenerateMinable.EventType.*;
import rtg.api.RTGAPI;
import rtg.api.config.RTGConfig;
import rtg.api.util.Acceptor;
import rtg.api.util.ChunkOreGenTracker;
import rtg.api.util.RandomUtil;
import rtg.util.Logger;
import rtg.util.SaplingUtil;
import rtg.world.WorldTypeRTG;
import rtg.world.biome.BiomeProviderRTG;
import rtg.world.biome.realistic.RealisticBiomeBase;
import rtg.world.gen.feature.tree.rtg.TreeRTG;
import rtg.world.gen.genlayer.RiverRemover;
@SuppressWarnings({"WeakerAccess", "unused"})
public class EventManagerRTG {
// Event handlers.
private final WorldEventRTG WORLD_EVENT_HANDLER = new WorldEventRTG();
private final LoadChunkRTG LOAD_CHUNK_EVENT_HANDLER = new LoadChunkRTG();
private final GenerateMinableRTG GENERATE_MINABLE_EVENT_HANDLER = new GenerateMinableRTG();
private final InitBiomeGensRTG INIT_BIOME_GENS_EVENT_HANDLER = new InitBiomeGensRTG();
private final SaplingGrowTreeRTG SAPLING_GROW_TREE_EVENT_HANDLER = new SaplingGrowTreeRTG();
private final DecorateBiomeEventRTG DECORATE_BIOME_EVENT_HANDLER = new DecorateBiomeEventRTG();
private WeakHashMap<Integer, Acceptor<ChunkEvent.Load>> chunkLoadEvents = new WeakHashMap<>();
private long worldSeed;
private RTGConfig rtgConfig;
public EventManagerRTG() {
rtgConfig = RTGAPI.config();
}
public class LoadChunkRTG {
LoadChunkRTG() {
logEventMessage("Initialising LoadChunkRTG...");
}
@SubscribeEvent
public void loadChunkRTG(ChunkEvent.Load event) {
// Are we in an RTG world?
if (!(event.getWorld().getWorldType() instanceof WorldTypeRTG)) {
return;
}
Acceptor<ChunkEvent.Load> acceptor = chunkLoadEvents.get(event.getWorld().provider.getDimension());
if (acceptor != null) {
acceptor.accept(event);
}
}
}
public class GenerateMinableRTG {
@SubscribeEvent
public void generateMinableRTG(OreGenEvent.GenerateMinable event) {
// Are we in an RTG world?
if (!(event.getWorld().getWorldType() instanceof WorldTypeRTG)) {
return;
}
ChunkOreGenTracker chunkOreGenTracker = WorldTypeRTG.chunkProvider.getChunkOreGenTracker();
BlockPos eventPos = event.getPos();
OreGenEvent.GenerateMinable.EventType eventType = event.getType();
String eventName = null;
boolean allowCancel = rtgConfig.ALLOW_ORE_GEN_EVENT_CANCELLATION.get();
// No switch statements allowed! - Pink
if (eventType == ANDESITE) {
eventName = "ANDESITE";
if (!rtgConfig.GENERATE_ORE_ANDESITE.get() || (chunkOreGenTracker.hasGeneratedOres(eventPos) && allowCancel)) {
event.setResult(Event.Result.DENY);
}
}
else if (eventType == COAL) {
eventName = "COAL";
if (!rtgConfig.GENERATE_ORE_COAL.get() || (chunkOreGenTracker.hasGeneratedOres(eventPos) && allowCancel)) {
event.setResult(Event.Result.DENY);
}
}
else if (eventType == DIAMOND) {
eventName = "DIAMOND";
if (!rtgConfig.GENERATE_ORE_DIAMOND.get() || (chunkOreGenTracker.hasGeneratedOres(eventPos) && allowCancel)) {
event.setResult(Event.Result.DENY);
}
}
else if (eventType == DIORITE) {
eventName = "DIORITE";
if (!rtgConfig.GENERATE_ORE_DIORITE.get() || (chunkOreGenTracker.hasGeneratedOres(eventPos) && allowCancel)) {
event.setResult(Event.Result.DENY);
}
}
else if (eventType == DIRT) {
eventName = "DIRT";
if (!rtgConfig.GENERATE_ORE_DIRT.get() || (chunkOreGenTracker.hasGeneratedOres(eventPos) && allowCancel)) {
event.setResult(Event.Result.DENY);
}
}
else if (eventType == EMERALD) {
eventName = "EMERALD";
if (!rtgConfig.GENERATE_ORE_EMERALD.get() || (chunkOreGenTracker.hasGeneratedOres(eventPos) && allowCancel)) {
event.setResult(Event.Result.DENY);
}
}
else if (eventType == GOLD) {
eventName = "GOLD";
if (!rtgConfig.GENERATE_ORE_GOLD.get() || (chunkOreGenTracker.hasGeneratedOres(eventPos) && allowCancel)) {
event.setResult(Event.Result.DENY);
}
}
else if (eventType == GRANITE) {
eventName = "GRANITE";
if (!rtgConfig.GENERATE_ORE_GRANITE.get() || (chunkOreGenTracker.hasGeneratedOres(eventPos) && allowCancel)) {
event.setResult(Event.Result.DENY);
}
}
else if (eventType == GRAVEL) {
eventName = "GRAVEL";
if (!rtgConfig.GENERATE_ORE_GRAVEL.get() || (chunkOreGenTracker.hasGeneratedOres(eventPos) && allowCancel)) {
event.setResult(Event.Result.DENY);
}
}
else if (eventType == IRON) {
eventName = "IRON";
if (!rtgConfig.GENERATE_ORE_IRON.get() || (chunkOreGenTracker.hasGeneratedOres(eventPos) && allowCancel)) {
event.setResult(Event.Result.DENY);
}
}
else if (eventType == LAPIS) {
eventName = "LAPIS";
if (!rtgConfig.GENERATE_ORE_LAPIS.get() || (chunkOreGenTracker.hasGeneratedOres(eventPos) && allowCancel)) {
event.setResult(Event.Result.DENY);
}
}
else if (eventType == REDSTONE) {
eventName = "REDSTONE";
if (!rtgConfig.GENERATE_ORE_REDSTONE.get() || (chunkOreGenTracker.hasGeneratedOres(eventPos) && allowCancel)) {
event.setResult(Event.Result.DENY);
}
}
else if (eventType == SILVERFISH) {
eventName = "SILVERFISH";
if (!rtgConfig.GENERATE_ORE_SILVERFISH.get() || (chunkOreGenTracker.hasGeneratedOres(eventPos) && allowCancel)) {
event.setResult(Event.Result.DENY);
}
}
//Logger.debug("%s EVENT @ %d %d %d (%d %d)", eventName, eventPos.getX(), eventPos.getY(), eventPos.getZ(), (eventPos.getX() / 16), (eventPos.getZ() / 16));
}
}
public class InitBiomeGensRTG {
@SubscribeEvent
public void initBiomeGensRTG(WorldTypeEvent.InitBiomeGens event) {
// Are we in an RTG world?
if (!(event.getWorldType() instanceof WorldTypeRTG)) {
return;
}
try {
event.setNewBiomeGens(new RiverRemover().riverLess(event.getOriginalBiomeGens()));
}
catch (ClassCastException ex) {
//throw ex;
// failed attempt because the GenLayers don't end with GenLayerRiverMix
}
}
}
public class SaplingGrowTreeRTG {
SaplingGrowTreeRTG() {
logEventMessage("Initialising SaplingGrowTreeRTG...");
}
@SubscribeEvent
public void saplingGrowTreeRTG(SaplingGrowTreeEvent event) {
// Are RTG saplings enabled?
if (!rtgConfig.ENABLE_RTG_SAPLINGS.get()) {
return;
}
// Are we in an RTG world? Do we have RTG's chunk manager?
if (!(event.getWorld().getWorldType() instanceof WorldTypeRTG) ||
!(event.getWorld().getBiomeProvider() instanceof BiomeProviderRTG)) {
return;
}
// Should we generate a vanilla tree instead?
if (event.getRand().nextInt(rtgConfig.RTG_TREE_CHANCE.get()) != 0) {
Logger.debug("Skipping RTG tree generation.");
return;
}
IBlockState saplingBlock = event.getWorld().getBlockState(event.getPos());
// Are we dealing with a sapling? Sounds like a silly question, but apparently it's one that needs to be asked.
if (!(saplingBlock.getBlock() instanceof BlockSapling)) {
Logger.debug("Could not get sapling meta from non-sapling BlockState (%s).", saplingBlock.getBlock().getLocalizedName());
return;
}
BiomeProviderRTG cmr = (BiomeProviderRTG) event.getWorld().getBiomeProvider();
//Biome bgg = cmr.getBiomeGenAt(x, z);
Biome bgg = event.getWorld().getBiome(event.getPos());
RealisticBiomeBase rb = RealisticBiomeBase.getBiome(Biome.getIdForBiome(bgg));
// Instead of patching the biome, we should just return early here to allow vanilla logic to kick in.
if (rb == null) {
Logger.debug("NULL biome (%d) found when trying to grow an RTG tree from a sapling.", Biome.getIdForBiome(bgg));
return;
}
ArrayList<TreeRTG> biomeTrees = rb.rtgTrees;
int saplingMeta = SaplingUtil.getMetaFromState(saplingBlock);
Logger.debug("Biome = %s", rb.baseBiome.getBiomeName());
Logger.debug("Ground Sapling Block = %s", saplingBlock.getBlock().getLocalizedName());
Logger.debug("Ground Sapling Meta = %d", saplingMeta);
if (biomeTrees.size() > 0) {
// First, let's get all of the trees in this biome that match the sapling on the ground.
ArrayList<TreeRTG> validTrees = new ArrayList<>();
for (int i = 0; i < biomeTrees.size(); i++) {
Logger.debug("Biome Tree #%d = %s", i, biomeTrees.get(i).getClass().getName());
Logger.debug("Biome Tree #%d Sapling Block = %s", i, biomeTrees.get(i).getSaplingBlock().getBlock().getLocalizedName());
Logger.debug("Biome Tree #%d Sapling Meta = %d", i, SaplingUtil.getMetaFromState(biomeTrees.get(i).getSaplingBlock()));
if (saplingBlock.getBlock() == biomeTrees.get(i).getSaplingBlock().getBlock()) {
if (SaplingUtil.getMetaFromState(saplingBlock) == SaplingUtil.getMetaFromState(biomeTrees.get(i).getSaplingBlock())) {
validTrees.add(biomeTrees.get(i));
Logger.debug("Valid tree found!");
}
}
}
// If there are valid trees, then proceed; otherwise, let's get out here.
if (validTrees.size() > 0) {
// Prevent the original tree from generating.
event.setResult(Event.Result.DENY);
// Get a random tree from the list of valid trees.
TreeRTG tree = validTrees.get(event.getRand().nextInt(validTrees.size()));
Logger.debug("Tree = %s", tree.getClass().getName());
// Set the trunk size if min/max values have been set.
if (tree.getMinTrunkSize() > 0 && tree.getMaxTrunkSize() > tree.getMinTrunkSize()) {
tree.setTrunkSize(RandomUtil.getRandomInt(event.getRand(), tree.getMinTrunkSize(), tree.getMaxTrunkSize()));
}
// Set the crown size if min/max values have been set.
if (tree.getMinCrownSize() > 0 && tree.getMaxCrownSize() > tree.getMinCrownSize()) {
tree.setCrownSize(RandomUtil.getRandomInt(event.getRand(), tree.getMinCrownSize(), tree.getMaxCrownSize()));
}
int treeHeight = tree.getTrunkSize() + tree.getCrownSize();
if (treeHeight < 1) {
Logger.debug("Unable to grow RTG tree with no height.");
return;
}
if (!tree.hasSpaceToGrow(event.getWorld(), event.getRand(), event.getPos(), treeHeight)) {
Logger.debug("Unable to grow RTG tree with %d height. Something in the way.", treeHeight);
return;
}
/*
* Set the generateFlag to what it needs to be for growing trees from saplings,
* generate the tree, and then set it back to what it was before.
*
* TODO: Does this affect the generation of normal RTG trees? - Pink
*/
int oldFlag = tree.getGenerateFlag();
tree.setGenerateFlag(3);
boolean generated = tree.generate(event.getWorld(), event.getRand(), event.getPos());
tree.setGenerateFlag(oldFlag);
if (generated) {
// Sometimes we have to remove the sapling manually because some trees grow around it, leaving the original sapling.
if (event.getWorld().getBlockState(event.getPos()) == saplingBlock) {
event.getWorld().setBlockState(event.getPos(), Blocks.AIR.getDefaultState(), 2);
}
}
}
else {
Logger.debug("There are no RTG trees associated with the sapling on the ground." +
" Generating a vanilla tree instead.");
}
}
}
}
public class WorldEventRTG {
WorldEventRTG() {
logEventMessage("Initialising WorldEventRTG...");
}
@SubscribeEvent
public void onWorldLoad(WorldEvent.Load event) {
// This event fires for each dimension loaded (and then one last time in which it returns 0?),
// so initialise a field to 0 and set it to the world seed and only display it in the log once.
if (worldSeed != event.getWorld().getSeed() && event.getWorld().getSeed() != 0) {
worldSeed = event.getWorld().getSeed();
Logger.info("World Seed: " + worldSeed);
}
}
@SubscribeEvent
public void onWorldUnload(WorldEvent.Unload event) {
// Reset the world seed so that it logs on the next server start if the seed is the same as the last load.
worldSeed = 0;
}
}
public class DecorateBiomeEventRTG {
DecorateBiomeEventRTG() {
logEventMessage("Initialising DecorateBiomeEventRTG...");
}
@SubscribeEvent
public void onBiomeDecorate(DecorateBiomeEvent.Decorate event) {
// Are flowing liquid modifications enabled?
// Note: This will need to move to the switch statement below if we add more case statements.
if (!rtgConfig.ENABLE_FLOWING_LIQUID_MODIFICATIONS.get()) {
return;
}
// Are we in an RTG world? Do we have RTG's chunk manager?
if (!(event.getWorld().getWorldType() instanceof WorldTypeRTG) ||
!(event.getWorld().getBiomeProvider() instanceof BiomeProviderRTG)) {
return;
}
DecorateBiomeEvent.Decorate.EventType eventType = event.getType();
// No switch statements allowed! - Pink
/*
* Vanilla generates flowing liquids during biome decoration,
* so we're going to cancel that event here and generate them via rPopulatePostDecorate().
*/
if (eventType == LAKE_WATER || eventType == LAKE_LAVA) {
event.setResult(Event.Result.DENY);
}
}
}
/*
* This method registers most of RTG's event handlers.
*
* We don't need to check if the event handlers are unregistered before registering them
* because Forge already performs those checks. This means that we could execute this method a
* million times, and each event handler would still only be registered once.
*/
public void registerEventHandlers() {
logEventMessage("Registering RTG's event handlers...");
MinecraftForge.EVENT_BUS.register(WORLD_EVENT_HANDLER);
MinecraftForge.EVENT_BUS.register(LOAD_CHUNK_EVENT_HANDLER);
MinecraftForge.ORE_GEN_BUS.register(GENERATE_MINABLE_EVENT_HANDLER);
MinecraftForge.TERRAIN_GEN_BUS.register(INIT_BIOME_GENS_EVENT_HANDLER);
MinecraftForge.TERRAIN_GEN_BUS.register(SAPLING_GROW_TREE_EVENT_HANDLER);
MinecraftForge.TERRAIN_GEN_BUS.register(DECORATE_BIOME_EVENT_HANDLER);
logEventMessage("RTG's event handlers have been registered successfully.");
}
private static void logEventMessage(String message) {
Logger.debug("RTG Event System: " + message);
}
public void setDimensionChunkLoadEvent(int dimension, Acceptor<ChunkEvent.Load> action) {
chunkLoadEvents.put(dimension, action);
}
}