package net.glowstone.generator.structures.util;
import net.glowstone.generator.objects.RandomItemsContent;
import net.glowstone.generator.structures.GlowStructurePiece;
import net.glowstone.util.BlockStateDelegate;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.BlockState;
import org.bukkit.block.CreatureSpawner;
import org.bukkit.entity.EntityType;
import org.bukkit.material.DirectionalContainer;
import org.bukkit.material.MaterialData;
import org.bukkit.util.Vector;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
public class StructureBuilder {
private final World world;
private final BlockStateDelegate delegate;
private final StructureBoundingBox boundingBox;
private final GlowStructurePiece structure;
public StructureBuilder(World world, GlowStructurePiece structure, StructureBoundingBox boundingBox, BlockStateDelegate delegate) {
this.world = world;
this.delegate = delegate;
this.boundingBox = boundingBox;
this.structure = structure;
}
public void addRandomMaterial(Map<StructureMaterial, Integer> materials, int weigth, Material type, int data) {
materials.put(new StructureMaterial(type, data), weigth);
}
public StructureMaterial getRandomMaterial(Random random, Map<StructureMaterial, Integer> materials) {
int totalWeight = 0;
for (int weigth : materials.values()) {
totalWeight += weigth;
}
int weight = random.nextInt(totalWeight);
for (Entry<StructureMaterial, Integer> entry : materials.entrySet()) {
weight -= entry.getValue();
if (weight < 0) {
return entry.getKey();
}
}
return new StructureMaterial(Material.AIR);
}
public BlockState getBlockState(Vector pos) {
final Vector vec = translate(pos);
return delegate.getBlockState(world, vec.getBlockX(), vec.getBlockY(), vec.getBlockZ());
}
public void setBlock(Vector pos, Material type) {
setBlock(pos, type, 0);
}
public void setBlock(Vector pos, Material type, int data) {
final Vector vec = translate(pos);
if (boundingBox.isVectorInside(vec)) {
delegate.setTypeAndRawData(world, vec.getBlockX(), vec.getBlockY(), vec.getBlockZ(), type, data);
}
}
public void setBlock(Vector pos, Material type, MaterialData data) {
final Vector vec = translate(pos);
if (boundingBox.isVectorInside(vec)) {
delegate.setTypeAndData(world, vec.getBlockX(), vec.getBlockY(), vec.getBlockZ(), type, data);
}
}
public void setBlockDownward(Vector pos, Material type) {
setBlockDownward(pos, type, 0);
}
public void setBlockDownward(Vector pos, Material type, int data) {
final Vector vec = translate(pos);
if (boundingBox.isVectorInside(vec)) {
int y = vec.getBlockY();
while (!world.getBlockAt(vec.getBlockX(), y, vec.getBlockZ()).getType().isSolid() && y > 1) {
delegate.setTypeAndRawData(world, vec.getBlockX(), y, vec.getBlockZ(), type, data);
y--;
}
}
}
public void setBlockDownward(Vector pos, Material type, MaterialData data) {
final Vector vec = translate(pos);
if (boundingBox.isVectorInside(vec)) {
int y = vec.getBlockY();
while (!world.getBlockAt(vec.getBlockX(), y, vec.getBlockZ()).getType().isSolid() && y > 1) {
delegate.setTypeAndData(world, vec.getBlockX(), y, vec.getBlockZ(), type, data);
y--;
}
}
}
public void setBlockWithRandomMaterial(Vector pos, Random random, Map<StructureMaterial, Integer> materials) {
final StructureMaterial material = getRandomMaterial(random, materials);
setBlock(pos, material.getType(), material.getData());
}
public void fill(Vector min, Vector max, Material type) {
fill(min, max, type, 0);
}
public void fill(Vector min, Vector max, Material type, int data) {
fill(min, max, type, data, type, data);
}
public void fill(Vector min, Vector max, Material type, MaterialData data) {
fill(min, max, type, data, type, data);
}
public void fill(Vector min, Vector max, Material outerType, Material innerType) {
fill(min, max, outerType, 0, innerType, 0);
}
public void fill(Vector min, Vector max, Material outerType, Material innerType, int innerData) {
fill(min, max, outerType, 0, innerType, innerData);
}
public void fill(Vector min, Vector max, Material outerType, Material innerType, MaterialData innerData) {
fill(min, max, outerType, 0, innerType, innerData);
}
public void fill(Vector min, Vector max, Material outerType, int outerData, Material innerType, MaterialData innerData) {
fill(min, max, outerType, outerData, innerType, innerData);
}
public void fill(Vector min, Vector max, Material outerType, int outerData, Material innerType) {
fill(min, max, outerType, outerData, innerType, 0);
}
public void fill(Vector min, Vector max, Material outerType, MaterialData outerData, Material innerType) {
fill(min, max, outerType, outerData, innerType, 0);
}
public void fill(Vector min, Vector max, Material outerType, MaterialData outerData, Material innerType, int innerData) {
fill(min, max, outerType, outerData, innerType, innerData);
}
public void fill(Vector min, Vector max, Material outerType, int outerData, Material innerType, int innerData) {
for (int y = min.getBlockY(); y <= max.getBlockY(); y++) {
for (int x = min.getBlockX(); x <= max.getBlockX(); x++) {
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
final Material type;
final int data;
if (x != min.getBlockX() && x != max.getBlockX() &&
z != min.getBlockZ() && z != max.getBlockZ() &&
y != min.getBlockY() && y != max.getBlockY()) {
type = innerType;
data = innerData;
} else {
type = outerType;
data = outerData;
}
setBlock(new Vector(x, y, z), type, data);
}
}
}
}
public void fill(Vector min, Vector max, Material outerType, MaterialData outerData, Material innerType, MaterialData innerData) {
for (int y = min.getBlockY(); y <= max.getBlockY(); y++) {
for (int x = min.getBlockX(); x <= max.getBlockX(); x++) {
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
final Material type;
final MaterialData data;
if (x != min.getBlockX() && x != max.getBlockX() &&
z != min.getBlockZ() && z != max.getBlockZ() &&
y != min.getBlockY() && y != max.getBlockY()) {
type = innerType;
data = innerData;
} else {
type = outerType;
data = outerData;
}
setBlock(new Vector(x, y, z), type, data);
}
}
}
}
public void fillWithRandomMaterial(Vector min, Vector max, Random random, Map<StructureMaterial, Integer> materials) {
for (int y = min.getBlockY(); y <= max.getBlockY(); y++) {
for (int x = min.getBlockX(); x <= max.getBlockX(); x++) {
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
final StructureMaterial material = getRandomMaterial(random, materials);
setBlock(new Vector(x, y, z), material.getType(), material.getData());
}
}
}
}
public boolean createRandomItemsContainer(Vector pos, Random random, RandomItemsContent content, DirectionalContainer container, int maxStacks) {
final Vector vec = translate(pos);
if (boundingBox.isVectorInside(vec)) {
final BlockState state = world.getBlockAt(vec.getBlockX(), vec.getBlockY(), vec.getBlockZ()).getState();
delegate.backupBlockState(state.getBlock());
state.setType(container.getItemType());
state.setData(container);
state.update(true);
return content.fillContainer(random, container, state, maxStacks);
}
return false;
}
public void createMobSpawner(Vector pos, EntityType entityType) {
final Vector vec = translate(pos);
if (boundingBox.isVectorInside(vec)) {
BlockState state = world.getBlockAt(vec.getBlockX(), vec.getBlockY(), vec.getBlockZ()).getState();
delegate.backupBlockState(state.getBlock());
state.setType(Material.MOB_SPAWNER);
state.update(true);
state = world.getBlockAt(vec.getBlockX(), vec.getBlockY(), vec.getBlockZ()).getState();
if (state instanceof CreatureSpawner) {
((CreatureSpawner) state).setSpawnedType(entityType);
}
}
}
public boolean spawnMob(Vector pos, EntityType entityType) {
final Vector vec = translate(pos);
if (boundingBox.isVectorInside(vec)) {
return world.spawnEntity(new Location(world, vec.getBlockX(), vec.getBlockY(), vec.getBlockZ()), entityType) != null;
}
return false;
}
private Vector translate(Vector pos) {
final StructureBoundingBox boundingBox = structure.getBoundingBox();
switch (structure.getOrientation()) {
case EAST:
return new Vector(boundingBox.getMax().getBlockX() - pos.getBlockZ(),
boundingBox.getMin().getBlockY() + pos.getBlockY(),
boundingBox.getMin().getBlockZ() + pos.getBlockX());
case SOUTH:
return new Vector(boundingBox.getMin().getBlockX() + pos.getBlockX(),
boundingBox.getMin().getBlockY() + pos.getBlockY(),
boundingBox.getMax().getBlockZ() - pos.getBlockZ());
case WEST:
return new Vector(boundingBox.getMin().getBlockX() + pos.getBlockZ(),
boundingBox.getMin().getBlockY() + pos.getBlockY(),
boundingBox.getMin().getBlockZ() + pos.getBlockX());
default: // NORTH
return new Vector(boundingBox.getMin().getBlockX() + pos.getBlockX(),
boundingBox.getMin().getBlockY() + pos.getBlockY(),
boundingBox.getMin().getBlockZ() + pos.getBlockZ());
}
}
public class StructureMaterial {
private Material type;
private int data;
public StructureMaterial(Material type) {
this(type, 0);
}
public StructureMaterial(Material type, int data) {
this.type = type;
this.data = data;
}
public Material getType() {
return type;
}
public int getData() {
return data;
}
}
}