Use independent street and avenue settings

This commit is contained in:
2026-09-27 08:11:52 -07:00
parent d68f7543ea
commit 8d99c2e67f
14 changed files with 425 additions and 161 deletions

View File

@@ -101,14 +101,23 @@ public class MetropolisPlugin extends JavaPlugin {
private int gridSizeX = 28;
private int gridSizeY = 256;
private int maxLevels = 1;
private int gridSizeZ = 28;
private int roadWidth = 4;
private int streetWidth = 4;
private int avenueWidth = 4;
private int gridSizeZ = 28;
private int roadLevel = 62;
private int spaceAboveRoad = 2;
private Material roadMaterial = Material.COBBLESTONE;
private boolean generateRoadSupports = true;
private Material roadSupportMaterial = Material.STONE;
private int avenueLevel = 62;
private int streetSpaceAbove = 2;
private int avenueSpaceAbove = 2;
private Material streetMaterial = Material.COBBLESTONE;
private Material avenueMaterial = Material.COBBLESTONE;
private boolean generateStreetSupports = true;
private boolean generateAvenueSupports = true;
private Material streetSupportMaterial = Material.STONE;
private Material avenueSupportMaterial = Material.STONE;
private boolean generateStreetStairs;
private Material streetStairMaterial = Material.COBBLESTONE_STAIRS;
private int streetStairWidth;
private int streetStairsEveryNBlocks;
private boolean generateAvenueStairs;
private Material avenueStairMaterial = Material.COBBLESTONE_STAIRS;
private int avenueStairWidth;
@@ -199,32 +208,44 @@ public class MetropolisPlugin extends JavaPlugin {
generateFloorSupports = safeGetBooleanFromConfig(config, "plot.floor.supports.generate");
floorSupportMaterial = safeGetMaterialFromConfig(config, "plot.floor.supports.material");
generateSign = safeGetBooleanFromConfig(config, "plot.sign.generate");
roadWidth =
safeGetIntFromConfig(config, "road.streets.width", "road.avenues.width", "road.width");
streetWidth = safeGetIntFromConfig(config, "road.streets.width");
avenueWidth = safeGetIntFromConfig(config, "road.avenues.width");
spaceAboveRoad =
safeGetIntFromConfig(
config,
"road.streets.clearSpaceAbove",
"road.avenues.clearSpaceAbove",
"road.clearSpaceAbove");
roadLevel =
safeGetIntFromConfig(config, "road.streets.level", "road.avenues.level", "road.level");
roadMaterial =
safeGetMaterialFromConfig(
config, "road.streets.material", "road.avenues.material", "road.material");
generateRoadSupports =
safeGetBooleanFromConfig(
config,
"road.streets.supports.generate",
"road.avenues.supports.generate",
"road.supports.generate");
roadSupportMaterial =
safeGetMaterialFromConfig(
config,
"road.streets.supports.material",
"road.avenues.supports.material",
"road.supports.material");
streetSpaceAbove = safeGetIntFromConfig(config, "road.streets.clearSpaceAbove");
avenueSpaceAbove = safeGetIntFromConfig(config, "road.avenues.clearSpaceAbove");
roadLevel = safeGetIntFromConfig(config, "road.streets.level");
avenueLevel = safeGetIntFromConfig(config, "road.avenues.level");
streetMaterial = safeGetMaterialFromConfig(config, "road.streets.material");
avenueMaterial = safeGetMaterialFromConfig(config, "road.avenues.material");
generateStreetSupports = safeGetBooleanFromConfig(config, "road.streets.supports.generate");
generateAvenueSupports = safeGetBooleanFromConfig(config, "road.avenues.supports.generate");
streetSupportMaterial = safeGetMaterialFromConfig(config, "road.streets.supports.material");
avenueSupportMaterial = safeGetMaterialFromConfig(config, "road.avenues.supports.material");
generateStreetStairs = safeGetBooleanFromConfig(config, "road.streets.stairs.generate");
if (generateStreetStairs) {
streetStairMaterial = safeGetMaterialFromConfig(config, "road.streets.stairs.material");
streetStairWidth = safeGetIntFromConfig(config, "road.streets.stairs.width");
streetStairsEveryNBlocks = safeGetIntFromConfig(config, "road.streets.stairs.everyNBlocks");
if (streetStairWidth < 1 || streetStairWidth >= streetWidth) {
getLogger()
.severe(
"road.streets.stairs.width must be positive and narrower than road.streets.width;"
+ " street stairs are disabled.");
generateStreetStairs = false;
}
if (streetStairsEveryNBlocks < 1) {
getLogger()
.severe(
"road.streets.stairs.everyNBlocks must be positive; street stairs are disabled.");
generateStreetStairs = false;
}
if (!(streetStairMaterial.createBlockData() instanceof Stairs)) {
getLogger()
.severe(
"road.streets.stairs.material must be a stair block; street stairs are disabled.");
generateStreetStairs = false;
}
}
generateAvenueStairs = safeGetBooleanFromConfig(config, "road.avenues.stairs.generate");
if (generateAvenueStairs) {
avenueStairMaterial = safeGetMaterialFromConfig(config, "road.avenues.stairs.material");
@@ -305,9 +326,12 @@ public class MetropolisPlugin extends JavaPlugin {
throw new RuntimeException(String.format("The world %s does not exist", worldName));
}
gridSizeX = plotSizeX + roadWidth;
gridSizeY = Math.max(plotSizeY + roadWidth, 1);
gridSizeZ = plotSizeZ + roadWidth;
if (streetWidth < 0 || avenueWidth < 0) {
throw new IllegalArgumentException("Road widths must not be negative.");
}
gridSizeX = plotSizeX + avenueWidth;
gridSizeY = Math.max(plotSizeY + Math.max(streetWidth, avenueWidth), 1);
gridSizeZ = plotSizeZ + streetWidth;
regionManager =
WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world));
@@ -372,7 +396,8 @@ public class MetropolisPlugin extends JavaPlugin {
}
*/
getLogger().info(String.format("roadWidth = %d", roadWidth));
getLogger()
.info(String.format("streetWidth = %d, avenueWidth = %d", streetWidth, avenueWidth));
}
_occupiedPlots = new ArrayList<Plot>();
@@ -595,7 +620,8 @@ public class MetropolisPlugin extends JavaPlugin {
plotSizeX,
plotSizeY,
plotSizeZ,
roadWidth,
avenueWidth,
streetWidth,
roadLevel);
if (!SpawnLayout.withinBuildHeight(spawnCuboid, world.getMinHeight(), world.getMaxHeight())) {
getLogger()
@@ -702,7 +728,7 @@ public class MetropolisPlugin extends JavaPlugin {
public static int clampMaxLevels(int requestedLevels, int availableLevels) {
int normalizedRequested = normalizeMaxLevels(requestedLevels);
int normalizedAvailable = Math.max(1, availableLevels);
int normalizedAvailable = Math.max(0, availableLevels);
return Math.min(normalizedRequested, normalizedAvailable);
}
@@ -770,28 +796,46 @@ public class MetropolisPlugin extends JavaPlugin {
return;
}
if (roadWidth > 0) {
int y = plotCuboid.minY;
Set<AvenueStairwayLayout.Step> stairOpenings = getUpperRoadOpenings(plotCuboid, y);
for (RoadLayout.BlockPosition roadBlock : RoadLayout.surroundingRoad(plotCuboid, roadWidth)) {
setRoad(roadBlock.x(), y, roadBlock.z(), stairOpenings);
if (streetWidth > 0 || avenueWidth > 0) {
for (RoadLayout.BlockPosition roadBlock :
RoadLayout.surroundingRoad(plotCuboid, streetWidth, avenueWidth)) {
int roadY =
roadBlock.roadType() == RoadLayout.RoadType.AVENUE
? plotCuboid.minY + avenueLevel - roadLevel
: plotCuboid.minY;
Set<AvenueStairwayLayout.Step> stairOpenings =
getUpperRoadOpenings(plotCuboid, roadY, roadBlock.roadType());
setRoad(roadBlock.x(), roadY, roadBlock.z(), roadBlock.roadType(), stairOpenings);
}
int upperLevel = Math.floorDiv(y - roadLevel, getLevelPitchY());
if (upperLevel > 0 && getLevelStartY(upperLevel) == y) {
generateAvenueStairs(plotCuboid, upperLevel - 1);
int upperLevel = Math.floorDiv(plotCuboid.minY - roadLevel, getLevelPitchY());
if (upperLevel > 0 && getLevelStartY(upperLevel) == plotCuboid.minY) {
generateRoadStairs(plotCuboid, upperLevel - 1, RoadLayout.RoadType.AVENUE);
generateRoadStairs(plotCuboid, upperLevel - 1, RoadLayout.RoadType.STREET);
}
}
}
private void setRoad(int x, int y, int z, Set<AvenueStairwayLayout.Step> stairOpenings) {
private void setRoad(
int x,
int y,
int z,
RoadLayout.RoadType roadType,
Set<AvenueStairwayLayout.Step> stairOpenings) {
if (stairOpenings.contains(new AvenueStairwayLayout.Step(x, y, z))) {
return;
}
boolean useAvenueSettings = roadType == RoadLayout.RoadType.AVENUE;
RoadBlockWriter.build(
world, x, y, z, roadMaterial, roadSupportMaterial, generateRoadSupports, spaceAboveRoad);
world,
x,
y,
z,
useAvenueSettings ? avenueMaterial : streetMaterial,
useAvenueSettings ? avenueSupportMaterial : streetSupportMaterial,
useAvenueSettings ? generateAvenueSupports : generateStreetSupports,
useAvenueSettings ? avenueSpaceAbove : streetSpaceAbove);
}
private boolean isSolidMaterial(Material material) {
@@ -853,6 +897,10 @@ public class MetropolisPlugin extends JavaPlugin {
int max = ring - (plotMultiplier - 1);
boolean done = false;
int levelCount = getEffectiveMaxLevels();
if (levelCount == 0) {
getLogger().severe("No configured plot level fits within the world's build height.");
return null;
}
while (!done) {
row = min;
@@ -921,11 +969,9 @@ public class MetropolisPlugin extends JavaPlugin {
}
}
if (DEBUG) {
getLogger().info(String.format("row: %d, col: %d", row, col));
}
return new Cuboid(
getPlotMin(row, col, plotMultiplier, 0), getPlotMax(row, col, plotMultiplier, 0));
getLogger()
.severe("Unable to find an available supported plot within the allocation search area.");
return null;
}
private boolean isAvailableSupportedPlot(int row, int col, int plotMultiplier, int level) {
@@ -995,7 +1041,7 @@ public class MetropolisPlugin extends JavaPlugin {
BlockVector3 bv =
BlockVector3.at(
gridMin.x() + roadWidth / 2, getLevelStartY(level), gridMin.z() + roadWidth / 2);
gridMin.x() + avenueWidth / 2, getLevelStartY(level), gridMin.z() + streetWidth / 2);
getLogger().info(String.format("getPlotMin (%d, %d, %d)", bv.x(), bv.y(), bv.z()));
return bv;
}
@@ -1009,9 +1055,9 @@ public class MetropolisPlugin extends JavaPlugin {
BlockVector3 bv =
BlockVector3.at(
gridMax.x() - (roadWidth - roadWidth / 2),
gridMax.x() - (avenueWidth - avenueWidth / 2),
getLevelStartY(level) + plotSizeY - 1,
gridMax.z() - (roadWidth - roadWidth / 2));
gridMax.z() - (streetWidth - streetWidth / 2));
getLogger().info(String.format("getPlotMax (%d, %d, %d)", bv.x(), bv.y(), bv.z()));
return bv;
}
@@ -1041,22 +1087,37 @@ public class MetropolisPlugin extends JavaPlugin {
}
private int getPlotXFromMin(Cuboid cuboid) {
return (cuboid.minX - roadWidth / 2) / gridSizeX;
return (cuboid.minX - avenueWidth / 2) / gridSizeX;
}
private int getPlotZFromMin(Cuboid cuboid) {
return (cuboid.minZ - roadWidth / 2) / gridSizeZ;
return (cuboid.minZ - streetWidth / 2) / gridSizeZ;
}
private int getLevelPitchY() {
return plotSizeY + roadWidth;
return plotSizeY + Math.max(streetWidth, avenueWidth);
}
private int getEffectiveMaxLevels() {
int maxWorldLevels = Math.max(1, (world.getMaxHeight() - roadLevel) / getLevelPitchY());
int availableStreetLevels = getAvailableLevelsAt(roadLevel);
int availableAvenueLevels =
avenueWidth > 0 ? getAvailableLevelsAt(avenueLevel) : availableStreetLevels;
int maxWorldLevels = Math.min(availableStreetLevels, availableAvenueLevels);
return clampMaxLevels(maxLevels, maxWorldLevels);
}
private int getAvailableLevelsAt(int baseY) {
if (world == null || baseY < world.getMinHeight() || plotSizeY < 1) {
return 0;
}
int highestPlotBase = world.getMaxHeight() - plotSizeY;
if (baseY > highestPlotBase) {
return 0;
}
return (highestPlotBase - baseY) / getLevelPitchY() + 1;
}
private int getLevelStartY(int level) {
return roadLevel + level * getLevelPitchY();
}
@@ -1104,6 +1165,9 @@ public class MetropolisPlugin extends JavaPlugin {
}
homeCuboid = findNextUnownedHomeRegion(multiplier);
if (homeCuboid == null) {
return null;
}
getLogger().info("Metropolis Generating home in " + homeCuboid.toString());
@@ -1195,37 +1259,28 @@ public class MetropolisPlugin extends JavaPlugin {
sign.update(true);
}
private void generateAvenueStairs(Cuboid plotCuboid, int lowerLevel) {
if (!generateAvenueStairs || lowerLevel < 0 || lowerLevel >= getEffectiveMaxLevels() - 1) {
return;
}
int lowerY = getLevelStartY(lowerLevel);
List<AvenueStairwayLayout.Flight> flights = getAvenueStairFlights(plotCuboid, lowerLevel);
private void generateRoadStairs(Cuboid plotCuboid, int lowerLevel, RoadLayout.RoadType roadType) {
List<AvenueStairwayLayout.Flight> flights =
getRoadStairFlights(plotCuboid, lowerLevel, roadType);
if (flights.isEmpty()) {
getLogger()
.warning(
"Avenue stair run does not fit beside plot at "
+ plotCuboid.minX
+ ","
+ plotCuboid.minY
+ ","
+ plotCuboid.minZ);
return;
}
Material stairMaterial =
roadType == RoadLayout.RoadType.AVENUE ? avenueStairMaterial : streetStairMaterial;
for (AvenueStairwayLayout.Flight flight : flights) {
AvenueStairwayBuilder.build(world, flight, avenueStairMaterial);
AvenueStairwayBuilder.build(world, flight, stairMaterial);
}
}
private Set<AvenueStairwayLayout.Step> getUpperRoadOpenings(Cuboid plotCuboid, int roadY) {
private Set<AvenueStairwayLayout.Step> getUpperRoadOpenings(
Cuboid plotCuboid, int roadY, RoadLayout.RoadType roadType) {
Set<AvenueStairwayLayout.Step> openings = new HashSet<>();
openings.addAll(getUpperRoadOpeningsForPlot(plotCuboid, roadY));
openings.addAll(getUpperRoadOpeningsForPlot(plotCuboid, roadY, roadType));
if (_occupiedPlots != null) {
for (Plot plot : _occupiedPlots) {
if (plot instanceof PlayerHome home && home.getCuboid().minY == roadY) {
openings.addAll(getUpperRoadOpeningsForPlot(home.getCuboid(), roadY));
if (plot instanceof PlayerHome home) {
openings.addAll(getUpperRoadOpeningsForPlot(home.getCuboid(), roadY, roadType));
}
}
}
@@ -1233,41 +1288,57 @@ public class MetropolisPlugin extends JavaPlugin {
}
private List<AvenueStairwayLayout.Step> getUpperRoadOpeningsForPlot(
Cuboid plotCuboid, int roadY) {
if (!generateAvenueStairs || roadY <= roadLevel) {
Cuboid plotCuboid, int roadY, RoadLayout.RoadType roadType) {
int baseY = roadType == RoadLayout.RoadType.AVENUE ? avenueLevel : roadLevel;
if (!areStairsEnabled(roadType) || roadY <= baseY) {
return List.of();
}
int upperLevel = Math.floorDiv(roadY - roadLevel, getLevelPitchY());
if (getLevelStartY(upperLevel) != roadY) {
int upperLevel = Math.floorDiv(roadY - baseY, getLevelPitchY());
if (baseY + upperLevel * getLevelPitchY() != roadY || upperLevel < 1) {
return List.of();
}
int lowerLevel = upperLevel - 1;
if (lowerLevel < 0) {
return List.of();
}
return getAvenueStairFlights(plotCuboid, lowerLevel).stream()
return getRoadStairFlights(plotCuboid, upperLevel - 1, roadType).stream()
.flatMap(flight -> flight.roadOpenings().stream())
.toList();
}
private List<AvenueStairwayLayout.Flight> getAvenueStairFlights(
Cuboid plotCuboid, int lowerLevel) {
if (!generateAvenueStairs || lowerLevel < 0 || lowerLevel >= getEffectiveMaxLevels() - 1) {
private boolean areStairsEnabled(RoadLayout.RoadType roadType) {
return roadType == RoadLayout.RoadType.AVENUE ? generateAvenueStairs : generateStreetStairs;
}
private List<AvenueStairwayLayout.Flight> getRoadStairFlights(
Cuboid plotCuboid, int lowerLevel, RoadLayout.RoadType roadType) {
if (!areStairsEnabled(roadType)
|| lowerLevel < 0
|| lowerLevel >= getEffectiveMaxLevels() - 1) {
return List.of();
}
return AvenueStairwayLayout.forUpperRoadSegments(
if (roadType == RoadLayout.RoadType.AVENUE) {
return AvenueStairwayLayout.forUpperRoadSegments(
plotCuboid,
plotSizeZ,
gridSizeZ,
getPlotZFromMin(plotCuboid),
avenueStairsEveryNBlocks,
avenueWidth,
avenueStairWidth,
avenueLevel + lowerLevel * getLevelPitchY(),
getLevelPitchY(),
3);
}
return AvenueStairwayLayout.forUpperStreetSegments(
plotCuboid,
plotSizeZ,
gridSizeZ,
getPlotZFromMin(plotCuboid),
avenueStairsEveryNBlocks,
avenueWidth,
avenueStairWidth,
getLevelStartY(lowerLevel),
plotSizeX,
gridSizeX,
getPlotXFromMin(plotCuboid),
streetStairsEveryNBlocks,
streetWidth,
streetStairWidth,
roadLevel + lowerLevel * getLevelPitchY(),
getLevelPitchY(),
3);
}