package electroblob.wizardry.worldgen; import com.google.common.collect.ImmutableMap; import electroblob.wizardry.Wizardry; import electroblob.wizardry.entity.living.EntityEvilWizard; import electroblob.wizardry.entity.living.EntityWizard; import electroblob.wizardry.integration.antiqueatlas.WizardryAntiqueAtlasIntegration; import electroblob.wizardry.util.GeometryUtils; import net.minecraft.block.BlockPlanks; import net.minecraft.block.BlockStainedHardenedClay; import net.minecraft.block.state.IBlockState; import net.minecraft.init.Biomes; import net.minecraft.init.Blocks; import net.minecraft.item.EnumDyeColor; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import net.minecraft.world.biome.Biome; import net.minecraft.world.gen.structure.template.ITemplateProcessor; import net.minecraft.world.gen.structure.template.PlacementSettings; import net.minecraft.world.gen.structure.template.Template; import net.minecraftforge.common.BiomeDictionary; import org.apache.commons.lang3.ArrayUtils; import java.util.HashSet; import java.util.Map; import java.util.Random; import java.util.Set; public class WorldGenWizardTower extends WorldGenSurfaceStructure { // TODO: Add wizard towers to the /locate command // This requires some careful manipulation of Random objects to replicate the positions exactly for the current // world. See the end of ChunkGeneratorOverworld for the relevant methods. private static final String WIZARD_DATA_BLOCK_TAG = "wizard"; private static final String EVIL_WIZARD_DATA_BLOCK_TAG = "evil_wizard"; private final Map SPECIAL_WALL_BLOCKS; public WorldGenWizardTower(){ // These are initialised here because it's a convenient point after the blocks are registered SPECIAL_WALL_BLOCKS = ImmutableMap.of( BiomeDictionary.Type.MESA, Blocks.RED_SANDSTONE.getDefaultState(), BiomeDictionary.Type.MOUNTAIN, Blocks.STONEBRICK.getDefaultState(), BiomeDictionary.Type.NETHER, Blocks.NETHER_BRICK.getDefaultState(), BiomeDictionary.Type.SANDY, Blocks.SANDSTONE.getDefaultState() ); } @Override public String getStructureName(){ return "wizard_tower"; } @Override public long getRandomSeedModifier(){ return 10473957L; // Yep, I literally typed 8 digits at random } @Override public boolean canGenerate(Random random, World world, int chunkX, int chunkZ){ return ArrayUtils.contains(Wizardry.settings.towerDimensions, world.provider.getDimension()) && Wizardry.settings.towerRarity > 0 && random.nextInt(Wizardry.settings.towerRarity) == 0; } @Override public ResourceLocation getStructureFile(Random random){ return random.nextDouble() < Wizardry.settings.evilWizardChance ? Wizardry.settings.towerWithChestFiles[random.nextInt(Wizardry.settings.towerWithChestFiles.length)] : Wizardry.settings.towerFiles[random.nextInt(Wizardry.settings.towerFiles.length)]; } @Override public void spawnStructure(Random random, World world, BlockPos origin, Template template, PlacementSettings settings, ResourceLocation structureFile){ final EnumDyeColor colour = EnumDyeColor.values()[random.nextInt(EnumDyeColor.values().length)]; final Biome biome = world.getBiome(origin); final IBlockState wallMaterial = SPECIAL_WALL_BLOCKS.keySet().stream().filter(t -> BiomeDictionary.hasType(biome, t)) .findFirst().map(SPECIAL_WALL_BLOCKS::get).orElse(Blocks.COBBLESTONE.getDefaultState()); final float mossiness = getBiomeMossiness(biome); final BlockPlanks.EnumType woodType = getBiomeWoodVariant(biome); final Set blocksPlaced = new HashSet<>(); ITemplateProcessor processor = new MultiTemplateProcessor(true, // Roof colour (w, p, i) -> i.blockState.getBlock() instanceof BlockStainedHardenedClay ? new Template.BlockInfo( i.pos, i.blockState.withProperty(BlockStainedHardenedClay.COLOR, colour), i.tileentityData) : i, // Wall material (w, p, i) -> i.blockState.getBlock() == Blocks.COBBLESTONE ? new Template.BlockInfo(i.pos, wallMaterial, i.tileentityData) : i, // Wood type new WoodTypeTemplateProcessor(woodType), // Mossifier new MossifierTemplateProcessor(mossiness, 0.04f, origin.getY() + 1), // Block recording (the process() method doesn't get called for structure voids) (w, p, i) -> {if(i.blockState.getBlock() != Blocks.AIR) blocksPlaced.add(p); return i;} ); template.addBlocksToWorld(world, origin, processor, settings, 2); WizardryAntiqueAtlasIntegration.markTower(world, origin.getX(), origin.getZ()); // Wizard spawning Map dataBlocks = template.getDataBlocks(origin, settings); for(Map.Entry entry : dataBlocks.entrySet()){ Vec3d vec = GeometryUtils.getCentre(entry.getKey()); if(entry.getValue().equals(WIZARD_DATA_BLOCK_TAG)){ EntityWizard wizard = new EntityWizard(world); wizard.setLocationAndAngles(vec.x, vec.y, vec.z, 0, 0); wizard.onInitialSpawn(world.getDifficultyForLocation(origin), null); wizard.setTowerBlocks(blocksPlaced); world.spawnEntity(wizard); }else if(entry.getValue().equals(EVIL_WIZARD_DATA_BLOCK_TAG)){ EntityEvilWizard wizard = new EntityEvilWizard(world); wizard.setLocationAndAngles(vec.x, vec.y, vec.z, 0, 0); wizard.hasStructure = true; // Stops it despawning wizard.onInitialSpawn(world.getDifficultyForLocation(origin), null); world.spawnEntity(wizard); }else{ // This probably shouldn't happen... Wizardry.logger.info("Unrecognised data block value {} in structure {}", entry.getValue(), structureFile); } } } private static float getBiomeMossiness(Biome biome){ if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.DENSE)) return 0.7f; if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.JUNGLE)) return 0.7f; if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.WET)) return 0.5f; if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.SWAMP)) return 0.5f; if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.FOREST)) return 0.3f; if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.LUSH)) return 0.3f; if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.DRY)) return 0; if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.COLD)) return 0; if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.DEAD)) return 0; if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.WASTELAND)) return 0; if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.NETHER)) return 0; return 0.1f; // Everything else (plains, etc.) has a small amount of moss } private static BlockPlanks.EnumType getBiomeWoodVariant(Biome biome){ // Unfortunately, I can't check all the wood types with the biome dictionary if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.CONIFEROUS)) return BlockPlanks.EnumType.SPRUCE; if(biome == Biomes.BIRCH_FOREST || biome == Biomes.BIRCH_FOREST_HILLS) return BlockPlanks.EnumType.BIRCH; if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.JUNGLE)) return BlockPlanks.EnumType.JUNGLE; if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.SAVANNA)) return BlockPlanks.EnumType.ACACIA; // Not technically a tree type, but I think it fits quite well anyway if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.SPOOKY)) return BlockPlanks.EnumType.DARK_OAK; // Everything else is oak return BlockPlanks.EnumType.OAK; } }