package com.thexfactor117.losteclipse.generation.procedural;
import java.util.ArrayList;
import java.util.Map.Entry;
import com.thexfactor117.losteclipse.LostEclipse;
import com.thexfactor117.losteclipse.init.ModLootTables;
import com.thexfactor117.losteclipse.util.Reference;
import net.minecraft.init.Blocks;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityChest;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.Rotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.WorldGenerator;
import net.minecraft.world.gen.structure.template.PlacementSettings;
import net.minecraft.world.gen.structure.template.Template;
import net.minecraft.world.gen.structure.template.TemplateManager;
public abstract class ProceduralDungeonBase extends WorldGenerator
{
protected int roomCount = 0;
protected int maxRooms;
private ArrayList<BlockPos> roomPositions;
private ArrayList<BlockPos> hallwayPositions;
public ProceduralDungeonBase(int maxRooms)
{
this.maxRooms = maxRooms;
this.roomPositions = new ArrayList<BlockPos>();
this.hallwayPositions = new ArrayList<BlockPos>();
}
public ArrayList<PotentialPosition> generateStartingRoom(TemplateManager manager, World world, BlockPos center, int roomCount)
{
for (BlockPos position : roomPositions)
{
if (position.equals(center))
return null;
}
LostEclipse.LOGGER.info("Generating starting room.");
ArrayList<PotentialPosition> nextPotentialPositions = new ArrayList<PotentialPosition>();
Rotation rotation = Rotation.values()[(int) (Math.random() * 4)];
ArrayList<PotentialPosition> nextPositions = generateRandomRoom(manager, world, center, rotation);
for (PotentialPosition position : nextPositions)
{
if (position != null)
nextPotentialPositions.add(position);
}
return nextPotentialPositions;
}
public ArrayList<PotentialPosition> generateRooms(TemplateManager manager, World world, ArrayList<PotentialPosition> potentialPositions, int roomCount)
{
LostEclipse.LOGGER.info("Potential size: " + potentialPositions.size());
ArrayList<PotentialPosition> nextPotentialPositions = new ArrayList<PotentialPosition>();
for (int i = 0; i < potentialPositions.size(); i++)
{
BlockPos center = potentialPositions.get(i).getPos();
Rotation rotation = potentialPositions.get(i).getRotation();
LostEclipse.LOGGER.info("Generating randomized rooms off of hallways.");
ArrayList<PotentialPosition> nextPositions = generateRandomRoom(manager, world, center, rotation);
if (nextPositions != null)
{
for (PotentialPosition position : nextPositions)
{
if (position != null)
nextPotentialPositions.add(position);
}
}
}
return nextPotentialPositions;
}
private ArrayList<PotentialPosition> generateRandomRoom(TemplateManager manager, World world, BlockPos center, Rotation rotation)
{
// if there is a room at a potential position, return.
for (int i = 0; i < roomPositions.size(); i++)
{
if (center.equals(roomPositions.get(i)))
{
LostEclipse.LOGGER.info("Room and potential position matched.");
return null;
}
}
// settings and such
PlacementSettings settings = new PlacementSettings().setRotation(rotation);
Template template = getRandomizedDungeonTemplate(manager, world);
// add blocks (and handle any data blocks)
BlockPos corner = translateToCorner(template, center, rotation); // translate from center to corner
template.addBlocksToWorld(world, corner, settings); // spawn in template at corner pos
handleDataBlocks(template, world, corner, settings); // update chest contents
roomPositions.add(center); // add template position to array
ArrayList<PotentialPosition> potentialPositions = new ArrayList<PotentialPosition>();
int hallways = (int) (Math.random() * 3 + 1);
LostEclipse.LOGGER.info("Hallways: " + hallways);
for (int i = 1; i <= hallways; i++)
{
boolean none = false;
boolean clockwise = false;
boolean clockwise180 = false;
boolean counterclockwise = false;
Rotation hallwayRotation = Rotation.values()[(int) (Math.random() * 4)];
LostEclipse.LOGGER.info("Hallway Rotation: " + hallwayRotation);
switch (hallwayRotation)
{
case NONE:
if (!none) potentialPositions.add(generateHallway(manager, world, center, hallwayRotation));
else potentialPositions.add(generateHallway(manager, world, center, hallwayRotation));
none = true;
break;
case CLOCKWISE_90:
if (!clockwise) potentialPositions.add(generateHallway(manager, world, center, hallwayRotation));
else potentialPositions.add(generateHallway(manager, world, center, hallwayRotation));
clockwise = true;
break;
case CLOCKWISE_180:
if (!clockwise180) potentialPositions.add(generateHallway(manager, world, center, hallwayRotation));
else potentialPositions.add(generateHallway(manager, world, center, hallwayRotation));
clockwise180 = true;
break;
case COUNTERCLOCKWISE_90:
if (!counterclockwise) potentialPositions.add(generateHallway(manager, world, center, hallwayRotation));
else potentialPositions.add(generateHallway(manager, world, center, hallwayRotation));
counterclockwise = true;
break;
}
}
for (int i = 0; i < potentialPositions.size(); i++)
{
if (potentialPositions.get(i) == null)
potentialPositions.remove(i);
}
return potentialPositions;
}
private PotentialPosition generateHallway(TemplateManager manager, World world, BlockPos roomCenter, Rotation rotation)
{
Template hallway = getRandomizedHallwayTemplate(manager, world); // get hallway and its center position
BlockPos hallwayCenter = getHallwayPosition(hallway, roomCenter, rotation);
for (BlockPos position : hallwayPositions) // check to make sure hallway can spawn. If not, exit.
{
if (position.equals(hallwayCenter))
return null;
}
PlacementSettings settings = new PlacementSettings().setRotation(rotation);
BlockPos hallwayCorner = translateHallwayToCorner(hallway, hallwayCenter, rotation);
hallway.addBlocksToWorld(world, hallwayCorner, settings); // add hallway into world at translated position
handleDataBlocks(hallway, world, hallwayCorner, settings);
hallwayPositions.add(hallwayCenter); // add hallway to hallwayPositions list
BlockPos potentialPosition = getRoomPosition(getRandomizedDungeonTemplate(manager, world), hallwayCenter, rotation);
return new PotentialPosition(potentialPosition, rotation);
}
private Template getRandomizedDungeonTemplate(TemplateManager manager, World world)
{
ArrayList<Template> templates = new ArrayList<Template>();
templates.add(manager.getTemplate(world.getMinecraftServer(), new ResourceLocation(Reference.MODID, "loot_room_1")));
templates.add(manager.getTemplate(world.getMinecraftServer(), new ResourceLocation(Reference.MODID, "loot_room_2")));
templates.add(manager.getTemplate(world.getMinecraftServer(), new ResourceLocation(Reference.MODID, "loot_room_3")));
return templates.get((int) (Math.random() * (templates.size())));
}
private Template getRandomizedHallwayTemplate(TemplateManager manager, World world)
{
ArrayList<Template> templates = new ArrayList<Template>();
templates.add(manager.getTemplate(world.getMinecraftServer(), new ResourceLocation(Reference.MODID, "hallway_1")));
templates.add(manager.getTemplate(world.getMinecraftServer(), new ResourceLocation(Reference.MODID, "hallway_2")));
templates.add(manager.getTemplate(world.getMinecraftServer(), new ResourceLocation(Reference.MODID, "hallway_3")));
return templates.get((int) (Math.random() * (templates.size())));
}
/**
* Iterates through every Data Structure Block in the given template. Used to add loot to chests.
* @param template
* @param world
* @param pos
* @param settings
*/
protected void handleDataBlocks(Template template, World world, BlockPos pos, PlacementSettings settings)
{
// loop through all data blocks within the structure
for (Entry<BlockPos, String> e : template.getDataBlocks(pos, settings).entrySet())
{
if ("common_chest".equals(e.getValue())) // check data block tag
{
BlockPos dataPos = e.getKey();
world.setBlockState(dataPos, Blocks.AIR.getDefaultState(), 3); // remove data block
TileEntity chestEntity = world.getTileEntity(dataPos.down()); // chest is located under data block
if (chestEntity instanceof TileEntityChest)
{
int rand = (int) (Math.random() * 100 + 1);
if (rand <= 85) ((TileEntityChest) chestEntity).setLootTable(ModLootTables.STRUCTURE_COMMON, world.rand.nextLong());
else if (rand > 95) ((TileEntityChest) chestEntity).setLootTable(ModLootTables.STRUCTURE_LEGENDARY, world.rand.nextLong());
else ((TileEntityChest) chestEntity).setLootTable(ModLootTables.STRUCTURE_RARE, world.rand.nextLong());
}
}
else if ("rare_chest".equals(e.getValue())) // check data block tag
{
BlockPos dataPos = e.getKey();
world.setBlockState(dataPos, Blocks.AIR.getDefaultState(), 3); // remove data block
TileEntity chestEntity = world.getTileEntity(dataPos.down()); // chest is located under data block
if (chestEntity instanceof TileEntityChest)
{
int rand = (int) (Math.random() * 100 + 1);
if (rand <= 40) ((TileEntityChest) chestEntity).setLootTable(ModLootTables.STRUCTURE_COMMON, world.rand.nextLong());
else if (rand > 90) ((TileEntityChest) chestEntity).setLootTable(ModLootTables.STRUCTURE_LEGENDARY, world.rand.nextLong());
else ((TileEntityChest) chestEntity).setLootTable(ModLootTables.STRUCTURE_RARE, world.rand.nextLong());
}
}
else if ("legendary_chest".equals(e.getValue())) // check data block tag
{
BlockPos dataPos = e.getKey();
world.setBlockState(dataPos, Blocks.AIR.getDefaultState(), 3); // remove data block
TileEntity chestEntity = world.getTileEntity(dataPos.down()); // chest is located under data block
if (chestEntity instanceof TileEntityChest)
{
int rand = (int) (Math.random() * 100 + 1);
if (rand <= 10) ((TileEntityChest) chestEntity).setLootTable(ModLootTables.STRUCTURE_COMMON, world.rand.nextLong());
else if (rand > 50) ((TileEntityChest) chestEntity).setLootTable(ModLootTables.STRUCTURE_LEGENDARY, world.rand.nextLong());
else ((TileEntityChest) chestEntity).setLootTable(ModLootTables.STRUCTURE_RARE, world.rand.nextLong());
}
}
}
}
protected BlockPos translateToCorner(Template template, BlockPos originalPos, Rotation rotation)
{
int x = originalPos.getX();
int z = originalPos.getZ();
switch (rotation)
{
case NONE:
x -= template.getSize().getX() / 2;
z -= template.getSize().getZ() / 2;
break;
case CLOCKWISE_90:
x += template.getSize().getX() / 2;
z -= template.getSize().getZ() / 2;
break;
case CLOCKWISE_180:
x += template.getSize().getX() / 2;
z += template.getSize().getZ() / 2;
break;
case COUNTERCLOCKWISE_90:
x -= template.getSize().getX() / 2;
z += template.getSize().getZ() / 2;
break;
}
return new BlockPos(x, originalPos.getY(), z);
}
protected BlockPos translateHallwayToCorner(Template template, BlockPos hallwayCenter, Rotation rotation)
{
int x = hallwayCenter.getX();
int z = hallwayCenter.getZ();
switch (rotation)
{
case NONE:
x -= template.getSize().getX() / 2;
z -= template.getSize().getZ() / 2;
break;
case CLOCKWISE_90:
x += template.getSize().getZ() / 2;
z -= template.getSize().getX() / 2;
break;
case CLOCKWISE_180:
x += template.getSize().getX() / 2;
z += template.getSize().getZ() / 2;
break;
case COUNTERCLOCKWISE_90:
x -= template.getSize().getZ() / 2;
z += template.getSize().getX() / 2;
break;
}
return new BlockPos(x, hallwayCenter.getY(), z);
}
protected BlockPos getHallwayPosition(Template template, BlockPos roomCenter, Rotation rotation)
{
int x = roomCenter.getX();
int z = roomCenter.getZ();
switch (rotation)
{
case NONE:
x += template.getSize().getX() - 1;
break;
case CLOCKWISE_90:
z += template.getSize().getX() - 1;
break;
case CLOCKWISE_180:
x -= template.getSize().getX() - 1;
break;
case COUNTERCLOCKWISE_90:
z -= template.getSize().getX() - 1;
break;
}
return new BlockPos(x, roomCenter.getY(), z);
}
protected BlockPos getRoomPosition(Template template, BlockPos hallwayCenter, Rotation rotation)
{
int x = hallwayCenter.getX();
int z = hallwayCenter.getZ();
switch (rotation)
{
case NONE:
x += template.getSize().getX() - 1;
break;
case CLOCKWISE_90:
z += template.getSize().getZ() - 1;
break;
case CLOCKWISE_180:
x -= template.getSize().getX() - 1;
break;
case COUNTERCLOCKWISE_90:
z -= template.getSize().getZ() - 1;
break;
}
return new BlockPos(x, hallwayCenter.getY(), z);
}
}