registerRenderer(BoundingBoxBeacon.class, new BeaconRenderer());
registerRenderer(BoundingBoxBiomeBorder.class, new BiomeBorderRenderer());
registerRenderer(BoundingBoxConduit.class, new ConduitRenderer());
+ registerRenderer(BoundingBoxSpawnableBlocks.class, new SpawnableBlocksRenderer());
registerProvider(new SlimeChunkProvider());
registerProvider(new WorldSpawnProvider());
registerProvider(new BiomeBorderProvider());
registerProvider(new MobSpawnerProvider());
registerProvider(new ConduitProvider());
+ registerProvider(new SpawnableBlocksProvider());
}
public static <T extends AbstractBoundingBox> void registerProvider(IBoundingBoxProvider<T> provider) {
registerType(BoundingBoxType.BiomeBorder, ConfigManager.drawBiomeBorders);
registerType(BoundingBoxType.Beacon, ConfigManager.drawBeacons);
registerType(BoundingBoxType.Conduit, ConfigManager.drawConduits);
+ registerType(BoundingBoxType.SpawnableBlocks, ConfigManager.drawSpawnableBlocks);
registerType(BoundingBoxType.JungleTemple, ConfigManager.drawJungleTemples);
registerType(BoundingBoxType.DesertTemple, ConfigManager.drawDesertTemples);
public static Setting<Boolean> drawRuinedPortals;
public static Setting<Boolean> drawConduits;
public static Setting<Boolean> renderConduitMobHarmArea;
+ public static Setting<Boolean> drawSpawnableBlocks;
+ public static Setting<Integer> spawnableBlocksRenderWidth;
+ public static Setting<Integer> spawnableBlocksRenderHeight;
public static void loadConfig() {
configDir = new File(".", "config");
renderAFKSpawnableBlocks = setup(config, "afkSpot", "renderAFKSpawnableBlocks", true, "If set to true boxes to show spawnable blocks within the AFK sphere will be drawn.");
afkSpawnableBlocksRenderDistance = setup(config, "afkSpot", "afkSpawnableBlocksRenderDistance", 3, "The distance from the player where spawnable blocks within the AFK sphere will be drawn.");
+ drawSpawnableBlocks = setup(config, "spawnableBlocks", "drawSpawnableBlocks", false, "If set to true boxes to show spawnable blocks will be drawn.");
+ spawnableBlocksRenderWidth = setup(config, "spawnableBlocks", "spawnableBlocksRenderWidth", 2, "The distance from the player where spawnable blocks will be drawn in X and Z axis.");
+ spawnableBlocksRenderHeight = setup(config, "spawnableBlocks", "spawnableBlocksRenderHeight", 1, "The distance from the player where spawnable blocks will be drawn in Y axis.");
+
config.save();
}
(x, width) -> new BoundingBoxTypeButton(width, I18n.format("bbor.features.beacons"), BoundingBoxType.Beacon),
(x, width) -> new BoundingBoxTypeButton(width, I18n.format("bbor.features.conduits"), BoundingBoxType.Conduit),
(x, width) -> new BoolSettingButton(width, I18n.format("bbor.features.conduits.mobHarmArea"), ConfigManager.renderConduitMobHarmArea))
+ .section(I18n.format("bbor.features.spawnableBlocks"),
+ (x, width) -> new BoundingBoxTypeButton(width, I18n.format("bbor.features.spawnableBlocks"), BoundingBoxType.SpawnableBlocks),
+ (x, width) -> new IntSettingSlider(width, 1, 3, "bbor.options.distance.y", ConfigManager.spawnableBlocksRenderHeight)
+ .addDisplayValue(1, "2")
+ .addDisplayValue(2, "4")
+ .addDisplayValue(3, "8"),
+ (x, width) -> new IntSettingSlider(width, 1, 3, "bbor.options.distance.xz", ConfigManager.spawnableBlocksRenderWidth)
+ .addDisplayValue(1, "8")
+ .addDisplayValue(2, "16")
+ .addDisplayValue(3, "32"))
.section(I18n.format("bbor.features.spawningSpheres"),
(x, width) -> new BoundingBoxTypeButton(width, I18n.format("bbor.features.spawningSpheres"), BoundingBoxType.AFKSphere),
(x, width) -> new BoolSettingButton(width, I18n.format("bbor.features.spawningSpheres.spawnableBlocks"), ConfigManager.renderAFKSpawnableBlocks),
--- /dev/null
+package com.irtimaled.bbor.client.interop;
+
+import com.irtimaled.bbor.common.models.Coords;
+import net.minecraft.block.Block;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.client.Minecraft;
+import net.minecraft.entity.EnumCreatureType;
+import net.minecraft.init.Blocks;
+import net.minecraft.tags.BlockTags;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.EnumLightType;
+import net.minecraft.world.World;
+import net.minecraft.world.biome.Biome;
+
+public class SpawnableBlocksHelper {
+ public static void findSpawnableBlocks(Coords coords, int width, int height, BlockProcessor blockProcessor) {
+ int blockX = coords.getX();
+ int minX = blockX - width;
+ int maxX = blockX + width + 1;
+
+ int blockZ = coords.getZ();
+ int minZ = blockZ - width;
+ int maxZ = blockZ + width + 1;
+
+ int blockY = coords.getY();
+ int minY = Math.max(1, blockY - height);
+ int maxY = Math.min(255, blockY + height);
+
+ World world = Minecraft.getInstance().world;
+ for (int x = minX; x < maxX; x++) {
+ for (int z = minZ; z < maxZ; z++) {
+ if (isBiomeHostileSpawnProof(world, new BlockPos(x, 1, z))) continue;
+
+ IBlockState upperBlockState = world.getBlockState(new BlockPos(x, minY - 1, z));
+ for (int y = Math.max(1, minY); y < maxY; y++) {
+ IBlockState spawnBlockState = upperBlockState;
+ BlockPos pos = new BlockPos(x, y, z);
+ upperBlockState = world.getBlockState(pos);
+ if (isSpawnable(world, pos, spawnBlockState, upperBlockState)) {
+ blockProcessor.process(x, y, z);
+ }
+ }
+ }
+ }
+ }
+
+ static boolean isBiomeHostileSpawnProof(World world, BlockPos pos) {
+ Biome biome = world.getBiome(pos);
+ return biome.getSpawningChance() == 0 ||
+ biome.getSpawns(EnumCreatureType.MONSTER).isEmpty();
+ }
+
+ static boolean isSpawnable(World world, BlockPos pos, IBlockState spawnBlockState, IBlockState upperBlockState) {
+ Block spawnBlock = spawnBlockState.getBlock();
+ return spawnBlock != Blocks.AIR &&
+ spawnBlock != Blocks.BEDROCK &&
+ spawnBlock != Blocks.BARRIER &&
+ spawnBlockState.isTopSolid() &&
+ !upperBlockState.isBlockNormalCube() &&
+ !upperBlockState.canProvidePower() &&
+ !upperBlockState.isIn(BlockTags.RAILS) &&
+ upperBlockState.getCollisionShape(world, pos).getEnd(EnumFacing.Axis.Y) <= 0 &&
+ upperBlockState.getFluidState().isEmpty() &&
+ (world.dimension.isNether() || world.getLightFor(EnumLightType.BLOCK, pos) <= 7);
+ }
+}
--- /dev/null
+package com.irtimaled.bbor.client.models;
+
+import com.irtimaled.bbor.common.BoundingBoxType;
+import com.irtimaled.bbor.common.models.AbstractBoundingBox;
+import com.irtimaled.bbor.common.models.Coords;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class BoundingBoxSpawnableBlocks extends AbstractBoundingBox {
+ private final Set<Coords> blocks = new HashSet<>();
+
+ public BoundingBoxSpawnableBlocks() {
+ super(BoundingBoxType.SpawnableBlocks);
+ }
+
+ public Set<Coords> getBlocks() {
+ return blocks;
+ }
+
+ @Override
+ public Boolean intersectsBounds(int minX, int minZ, int maxX, int maxZ) {
+ return true;
+ }
+}
super(point, SPAWN_RADIUS, BoundingBoxType.AFKSphere);
}
+ public boolean isWithinSphere(Point point) {
+ return this.getPoint().getDistance(point) <= getRadius() + 0.5D;
+ }
+
public void setSpawnableCount(int count) {
this.spawnableCount = count;
}
--- /dev/null
+package com.irtimaled.bbor.client.providers;
+
+import com.irtimaled.bbor.client.Player;
+import com.irtimaled.bbor.client.config.BoundingBoxTypeHelper;
+import com.irtimaled.bbor.client.config.ConfigManager;
+import com.irtimaled.bbor.client.interop.SpawnableBlocksHelper;
+import com.irtimaled.bbor.client.models.BoundingBoxSpawnableBlocks;
+import com.irtimaled.bbor.common.BoundingBoxType;
+import com.irtimaled.bbor.common.MathHelper;
+import com.irtimaled.bbor.common.models.Coords;
+import net.minecraft.client.Minecraft;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class SpawnableBlocksProvider implements IBoundingBoxProvider<BoundingBoxSpawnableBlocks> {
+ public static final Minecraft minecraft = Minecraft.getInstance();
+ private static Long lastGameTime = null;
+
+ private static Set<BoundingBoxSpawnableBlocks> lastBoundingBox = null;
+
+ public static void clear() {
+ lastBoundingBox = null;
+ }
+
+ private boolean isWithinActiveSpawningSphere() {
+ return BoundingBoxTypeHelper.shouldRender(BoundingBoxType.AFKSphere) &&
+ ConfigManager.renderAFKSpawnableBlocks.get() &&
+ SpawningSphereProvider.playerInsideSphere();
+ }
+
+ @Override
+ public boolean canProvide(int dimensionId) {
+ return BoundingBoxTypeHelper.shouldRender(BoundingBoxType.SpawnableBlocks) &&
+ !isWithinActiveSpawningSphere();
+ }
+
+ @Override
+ public Iterable<BoundingBoxSpawnableBlocks> get(int dimensionId) {
+ long gameTime = minecraft.world.getGameTime();
+ if (lastBoundingBox == null || (!((Long) gameTime).equals(lastGameTime) && gameTime % 2L == 0L)) {
+ lastGameTime = gameTime;
+ lastBoundingBox = getSpawnableBlocks();
+ }
+ return lastBoundingBox;
+ }
+
+ private Set<BoundingBoxSpawnableBlocks> getSpawnableBlocks() {
+ BoundingBoxSpawnableBlocks boundingBox = new BoundingBoxSpawnableBlocks();
+ Set<Coords> blocks = boundingBox.getBlocks();
+
+ int width = MathHelper.floor(Math.pow(2, 1 + ConfigManager.spawnableBlocksRenderWidth.get()));
+ int height = MathHelper.floor(Math.pow(2, ConfigManager.spawnableBlocksRenderHeight.get()));
+
+ SpawnableBlocksHelper.findSpawnableBlocks(Player.getCoords(), width, height,
+ (x, y, z) -> blocks.add(new Coords(x, y, z)));
+
+ Set<BoundingBoxSpawnableBlocks> boundingBoxes = new HashSet<>();
+ boundingBoxes.add(boundingBox);
+ return boundingBoxes;
+ }
+}
return SpawningSphereHelper.findSpawnableSpaces(center, center.getCoords(), size, size, (x, y, z) -> true);
}
+ static boolean playerInsideSphere() {
+ return spawningSphereInDimension(Player.getDimensionId()) && spawningSphere.isWithinSphere(Player.getPoint());
+ }
+
+ private static boolean spawningSphereInDimension(int dimensionId) {
+ return spawningSphere != null && SpawningSphereProvider.dimensionId == dimensionId;
+ }
+
@Override
public boolean canProvide(int dimensionId) {
- return spawningSphere != null && SpawningSphereProvider.dimensionId == dimensionId && BoundingBoxTypeHelper.shouldRender(BoundingBoxType.AFKSphere);
+ return spawningSphereInDimension(dimensionId) && BoundingBoxTypeHelper.shouldRender(BoundingBoxType.AFKSphere);
}
@Override
--- /dev/null
+package com.irtimaled.bbor.client.renderers;
+
+import com.irtimaled.bbor.client.models.BoundingBoxSpawnableBlocks;
+
+public class SpawnableBlocksRenderer extends AbstractRenderer<BoundingBoxSpawnableBlocks> {
+ @Override
+ public void render(BoundingBoxSpawnableBlocks boundingBox) {
+ boundingBox.getBlocks().forEach(c -> {
+ int x = c.getX();
+ int y = c.getY();
+ int z = c.getZ();
+ OffsetBox offsetBox = new OffsetBox(x, y, z, x + 1, y, z + 1);
+ renderCuboid(offsetBox, boundingBox.getColor());
+ });
+ }
+}
public static final BoundingBoxType Custom = register(Color.WHITE, "Custom");
public static final BoundingBoxType Beacon = register(Color.WHITE, "Beacon");
public static final BoundingBoxType Conduit = register(Color.CYAN, "Conduit");
+ public static final BoundingBoxType SpawnableBlocks = register(Color.RED, "Spawnable Blocks");
public static final BoundingBoxType JungleTemple = register(Colors.DARK_GREEN, "Jungle_Pyramid");
public static final BoundingBoxType DesertTemple = register(Color.ORANGE, "Desert_Pyramid");
"bbor.options.distance.nearest": "Nearest",
"bbor.options.distance.nearer": "Nearer",
"bbor.options.distance.normal": "Normal",
+ "bbor.options.distance.y": "Distance (Y): %s",
+ "bbor.options.distance.xz": "Distance (X/Z): %s",
"bbor.features.spawnChunks": "Spawn Chunks",
"bbor.features.lazyChunks": "Lazy Chunks",
"bbor.features.mobSpawners.activationLines": "Activation Lines",
"bbor.features.spawningSpheres": "Spawning Spheres",
"bbor.features.spawningSpheres.spawnableBlocks": "Spawnable Blocks",
+ "bbor.features.spawnableBlocks": "Spawnable Blocks",
"bbor.features.biomeBorders": "Biome Borders",
"bbor.sections.beaconsAndConduits": "Beacons & Conduits",