001/* 002 * PlotSquared, a land and world management plugin for Minecraft. 003 * Copyright (C) IntellectualSites <https://intellectualsites.com> 004 * Copyright (C) IntellectualSites team and contributors 005 * 006 * This program is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program. If not, see <https://www.gnu.org/licenses/>. 018 */ 019package com.plotsquared.core.generator; 020 021import com.google.common.base.Preconditions; 022import com.google.inject.Inject; 023import com.plotsquared.core.PlotSquared; 024import com.plotsquared.core.configuration.Settings; 025import com.plotsquared.core.inject.factory.HybridPlotWorldFactory; 026import com.plotsquared.core.location.Location; 027import com.plotsquared.core.plot.PlotArea; 028import com.plotsquared.core.plot.PlotId; 029import com.plotsquared.core.queue.ZeroedDelegateScopedQueueCoordinator; 030import com.plotsquared.core.util.MathMan; 031import com.sk89q.worldedit.entity.BaseEntity; 032import com.sk89q.worldedit.entity.Entity; 033import com.sk89q.worldedit.extent.Extent; 034import com.sk89q.worldedit.math.BlockVector3; 035import com.sk89q.worldedit.math.Vector3; 036import com.sk89q.worldedit.regions.CuboidRegion; 037import com.sk89q.worldedit.regions.RegionOperationException; 038import com.sk89q.worldedit.world.NullWorld; 039import com.sk89q.worldedit.world.biome.BiomeType; 040import com.sk89q.worldedit.world.block.BaseBlock; 041import com.sk89q.worldedit.world.block.BlockTypes; 042import org.checkerframework.checker.nullness.qual.NonNull; 043import org.checkerframework.checker.nullness.qual.Nullable; 044 045import java.util.EnumSet; 046 047public class HybridGen extends IndependentPlotGenerator { 048 049 private static final CuboidRegion CHUNK = new CuboidRegion(BlockVector3.ZERO, BlockVector3.at(15, 396, 15)); 050 private final HybridPlotWorldFactory hybridPlotWorldFactory; 051 052 @Inject 053 public HybridGen(final @NonNull HybridPlotWorldFactory hybridPlotWorldFactory) { 054 this.hybridPlotWorldFactory = hybridPlotWorldFactory; 055 } 056 057 @Override 058 public String getName() { 059 return PlotSquared.platform().pluginName(); 060 } 061 062 private void placeSchem( 063 HybridPlotWorld world, 064 ZeroedDelegateScopedQueueCoordinator result, 065 short relativeX, 066 short relativeZ, 067 int x, 068 int z, 069 EnumSet<SchematicFeature> features 070 ) { 071 int minY; // Math.min(world.PLOT_HEIGHT, world.ROAD_HEIGHT); 072 boolean isRoad = features.contains(SchematicFeature.ROAD); 073 if ((isRoad && Settings.Schematics.PASTE_ROAD_ON_TOP) || (!isRoad && Settings.Schematics.PASTE_ON_TOP)) { 074 minY = world.SCHEM_Y; 075 } else { 076 minY = world.getMinBuildHeight(); 077 } 078 BaseBlock[] blocks = world.G_SCH.get(MathMan.pair(relativeX, relativeZ)); 079 if (blocks != null) { 080 for (int y = 0; y < blocks.length; y++) { 081 if (blocks[y] != null) { 082 if (!features.contains(SchematicFeature.POPULATING) || blocks[y].hasNbtData()) { 083 result.setBlock(x, minY + y, z, blocks[y]); 084 } 085 } 086 } 087 } 088 if (!features.contains(SchematicFeature.BIOMES)) { 089 return; 090 } 091 BiomeType biome = world.G_SCH_B.get(MathMan.pair(relativeX, relativeZ)); 092 if (biome != null) { 093 result.setBiome(x, z, biome); 094 } 095 } 096 097 @Override 098 public void generateChunk(@NonNull ZeroedDelegateScopedQueueCoordinator result, @NonNull PlotArea settings, boolean biomes) { 099 Preconditions.checkNotNull(result, "result cannot be null"); 100 Preconditions.checkNotNull(settings, "settings cannot be null"); 101 102 HybridPlotWorld hybridPlotWorld = (HybridPlotWorld) settings; 103 // Biome 104 if (biomes) { 105 result.fillBiome(hybridPlotWorld.getPlotBiome()); 106 } 107 // Bedrock 108 if (hybridPlotWorld.PLOT_BEDROCK) { 109 for (short x = 0; x < 16; x++) { 110 for (short z = 0; z < 16; z++) { 111 result.setBlock(x, hybridPlotWorld.getMinGenHeight(), z, BlockTypes.BEDROCK.getDefaultState()); 112 } 113 } 114 } 115 EnumSet<SchematicFeature> roadFeatures = EnumSet.of(SchematicFeature.ROAD); 116 EnumSet<SchematicFeature> plotFeatures = EnumSet.noneOf(SchematicFeature.class); 117 if (biomes) { 118 roadFeatures.add(SchematicFeature.BIOMES); 119 plotFeatures.add(SchematicFeature.BIOMES); 120 } 121 122 // Coords 123 Location min = result.getMin(); 124 int bx = min.getX() - hybridPlotWorld.ROAD_OFFSET_X; 125 int bz = min.getZ() - hybridPlotWorld.ROAD_OFFSET_Z; 126 127 // The relative X-coordinate (within the plot) of the minimum X coordinate 128 // contained in the scoped queue 129 short relativeOffsetX = (short) Math.floorMod(bx, hybridPlotWorld.SIZE); 130 // The relative Z-coordinate (within the plot) of the minimum Z coordinate 131 // contained in the scoped queue 132 short relativeOffsetZ = (short) Math.floorMod(bz, hybridPlotWorld.SIZE); 133 134 // The X-coordinate of a given X coordinate, relative to the 135 // plot (Counting from the corner with the least positive 136 // coordinates) 137 short[] relativeX = new short[16]; 138 boolean[] insideRoadX = new boolean[16]; 139 boolean[] insideWallX = new boolean[16]; 140 short offsetX = relativeOffsetX; 141 for (short i = 0; i < 16; i++) { 142 if (offsetX >= hybridPlotWorld.SIZE) { 143 offsetX -= hybridPlotWorld.SIZE; 144 } 145 relativeX[i] = offsetX; 146 if (hybridPlotWorld.ROAD_WIDTH != 0) { 147 insideRoadX[i] = offsetX < hybridPlotWorld.PATH_WIDTH_LOWER || offsetX > hybridPlotWorld.PATH_WIDTH_UPPER; 148 insideWallX[i] = offsetX == hybridPlotWorld.PATH_WIDTH_LOWER || offsetX == hybridPlotWorld.PATH_WIDTH_UPPER; 149 } 150 offsetX++; 151 } 152 // The Z-coordinate of a given Z coordinate, relative to the 153 // plot (Counting from the corner with the least positive 154 // coordinates) 155 short[] relativeZ = new short[16]; 156 boolean[] insideRoadZ = new boolean[16]; 157 boolean[] insideWallZ = new boolean[16]; 158 short offsetZ = relativeOffsetZ; 159 for (short i = 0; i < 16; i++) { 160 if (offsetZ >= hybridPlotWorld.SIZE) { 161 offsetZ -= hybridPlotWorld.SIZE; 162 } 163 relativeZ[i] = offsetZ; 164 if (hybridPlotWorld.ROAD_WIDTH != 0) { 165 insideRoadZ[i] = offsetZ < hybridPlotWorld.PATH_WIDTH_LOWER || offsetZ > hybridPlotWorld.PATH_WIDTH_UPPER; 166 insideWallZ[i] = offsetZ == hybridPlotWorld.PATH_WIDTH_LOWER || offsetZ == hybridPlotWorld.PATH_WIDTH_UPPER; 167 } 168 offsetZ++; 169 } 170 // generation 171 int startY = hybridPlotWorld.getMinGenHeight() + (hybridPlotWorld.PLOT_BEDROCK ? 1 : 0); 172 for (short x = 0; x < 16; x++) { 173 if (insideRoadX[x]) { 174 for (short z = 0; z < 16; z++) { 175 // Road 176 for (int y = startY; y <= hybridPlotWorld.ROAD_HEIGHT; y++) { 177 result.setBlock(x, y, z, hybridPlotWorld.ROAD_BLOCK.toPattern()); 178 } 179 if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 180 placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, roadFeatures); 181 } 182 } 183 } else if (insideWallX[x]) { 184 for (short z = 0; z < 16; z++) { 185 if (insideRoadZ[z]) { 186 // road 187 for (int y = startY; y <= hybridPlotWorld.ROAD_HEIGHT; y++) { 188 result.setBlock(x, y, z, hybridPlotWorld.ROAD_BLOCK.toPattern()); 189 } 190 if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 191 placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, roadFeatures); 192 } 193 } else { 194 // wall 195 for (int y = startY; y <= hybridPlotWorld.WALL_HEIGHT; y++) { 196 result.setBlock(x, y, z, hybridPlotWorld.WALL_FILLING.toPattern()); 197 } 198 if (!hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 199 if (hybridPlotWorld.PLACE_TOP_BLOCK) { 200 result.setBlock(x, hybridPlotWorld.WALL_HEIGHT + 1, z, hybridPlotWorld.WALL_BLOCK.toPattern()); 201 } 202 } else { 203 placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, roadFeatures); 204 } 205 } 206 } 207 } else { 208 for (short z = 0; z < 16; z++) { 209 if (insideRoadZ[z]) { 210 // road 211 for (int y = startY; y <= hybridPlotWorld.ROAD_HEIGHT; y++) { 212 result.setBlock(x, y, z, hybridPlotWorld.ROAD_BLOCK.toPattern()); 213 } 214 if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 215 placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, roadFeatures); 216 } 217 } else if (insideWallZ[z]) { 218 // wall 219 for (int y = startY; y <= hybridPlotWorld.WALL_HEIGHT; y++) { 220 result.setBlock(x, y, z, hybridPlotWorld.WALL_FILLING.toPattern()); 221 } 222 if (!hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 223 if (hybridPlotWorld.PLACE_TOP_BLOCK) { 224 result.setBlock(x, hybridPlotWorld.WALL_HEIGHT + 1, z, hybridPlotWorld.WALL_BLOCK.toPattern()); 225 } 226 } else { 227 placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, roadFeatures); 228 } 229 } else { 230 // plot 231 for (int y = startY; y < hybridPlotWorld.PLOT_HEIGHT; y++) { 232 result.setBlock(x, y, z, hybridPlotWorld.MAIN_BLOCK.toPattern()); 233 } 234 result.setBlock(x, hybridPlotWorld.PLOT_HEIGHT, z, hybridPlotWorld.TOP_BLOCK.toPattern()); 235 if (hybridPlotWorld.PLOT_SCHEMATIC) { 236 placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, plotFeatures); 237 } 238 } 239 } 240 } 241 } 242 } 243 244 @Override 245 public void populateChunk(final ZeroedDelegateScopedQueueCoordinator result, final PlotArea settings) { 246 HybridPlotWorld hybridPlotWorld = (HybridPlotWorld) settings; 247 if (!hybridPlotWorld.populationNeeded()) { 248 return; 249 } 250 EnumSet<SchematicFeature> roadFeatures = EnumSet.of(SchematicFeature.POPULATING, SchematicFeature.ROAD); 251 EnumSet<SchematicFeature> plotFeatures = EnumSet.of(SchematicFeature.POPULATING); 252 253 // Coords 254 Location min = result.getMin(); 255 int bx = min.getX() - hybridPlotWorld.ROAD_OFFSET_X; 256 int bz = min.getZ() - hybridPlotWorld.ROAD_OFFSET_Z; 257 258 // The relative X-coordinate (within the plot) of the minimum X coordinate 259 // contained in the scoped queue 260 short relativeOffsetX = (short) Math.floorMod(bx, hybridPlotWorld.SIZE); 261 // The relative Z-coordinate (within the plot) of the minimum Z coordinate 262 // contained in the scoped queue 263 short relativeOffsetZ = (short) Math.floorMod(bz, hybridPlotWorld.SIZE); 264 265 boolean allRoad = true; 266 boolean overlap = false; 267 268 // The X-coordinate of a given X coordinate, relative to the 269 // plot (Counting from the corner with the least positive 270 // coordinates) 271 short[] relativeX = new short[16]; 272 boolean[] insideRoadX = new boolean[16]; 273 boolean[] insideWallX = new boolean[16]; 274 short offsetX = relativeOffsetX; 275 for (short i = 0; i < 16; i++) { 276 if (offsetX >= hybridPlotWorld.SIZE) { 277 offsetX -= hybridPlotWorld.SIZE; 278 overlap = true; 279 } 280 relativeX[i] = offsetX; 281 if (hybridPlotWorld.ROAD_WIDTH != 0) { 282 boolean insideRoad = offsetX < hybridPlotWorld.PATH_WIDTH_LOWER || offsetX > hybridPlotWorld.PATH_WIDTH_UPPER; 283 boolean insideWall = offsetX == hybridPlotWorld.PATH_WIDTH_LOWER || offsetX == hybridPlotWorld.PATH_WIDTH_UPPER; 284 insideRoadX[i] = insideRoad; 285 insideWallX[i] = insideWall; 286 allRoad &= insideRoad && insideWall; 287 } 288 offsetX++; 289 } 290 291 // The Z-coordinate of a given Z coordinate, relative to the 292 // plot (Counting from the corner with the least positive 293 // coordinates) 294 short[] relativeZ = new short[16]; 295 boolean[] insideRoadZ = new boolean[16]; 296 boolean[] insideWallZ = new boolean[16]; 297 short offsetZ = relativeOffsetZ; 298 for (short i = 0; i < 16; i++) { 299 if (offsetZ >= hybridPlotWorld.SIZE) { 300 offsetZ -= hybridPlotWorld.SIZE; 301 overlap = true; 302 } 303 relativeZ[i] = offsetZ; 304 if (hybridPlotWorld.ROAD_WIDTH != 0) { 305 boolean insideRoad = offsetZ < hybridPlotWorld.PATH_WIDTH_LOWER || offsetZ > hybridPlotWorld.PATH_WIDTH_UPPER; 306 boolean insideWall = offsetZ == hybridPlotWorld.PATH_WIDTH_LOWER || offsetZ == hybridPlotWorld.PATH_WIDTH_UPPER; 307 insideRoadZ[i] = insideRoad; 308 insideWallZ[i] = insideWall; 309 allRoad &= insideRoad && insideWall; 310 } 311 offsetZ++; 312 } 313 for (short x = 0; x < 16; x++) { 314 if (insideRoadX[x] || insideWallX[x]) { 315 if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 316 for (short z = 0; z < 16; z++) { 317 placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, roadFeatures); 318 } 319 } 320 } else { 321 for (short z = 0; z < 16; z++) { 322 if (insideRoadZ[z] || insideWallZ[z]) { 323 if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 324 placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, roadFeatures); 325 } 326 } else if (hybridPlotWorld.PLOT_SCHEMATIC) { 327 placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, plotFeatures); 328 } 329 } 330 } 331 } 332 if (!allRoad && hybridPlotWorld.getPlotSchematicEntities() != null && !hybridPlotWorld 333 .getPlotSchematicEntities() 334 .isEmpty()) { 335 CuboidRegion region = CHUNK.clone(); 336 try { 337 region.shift(hybridPlotWorld 338 .getPlotSchematicMinPoint() 339 .add(relativeOffsetX, 0, relativeOffsetZ) 340 .subtract(hybridPlotWorld.PATH_WIDTH_LOWER + 1, 0, hybridPlotWorld.PATH_WIDTH_LOWER + 1)); 341 for (Entity entity : hybridPlotWorld.getPlotSchematicEntities()) { 342 if (region.contains(entity.getLocation().toVector().toBlockPoint())) { 343 Vector3 pos = (entity.getLocation().toVector() 344 .subtract(region 345 .getMinimumPoint() 346 .withY(hybridPlotWorld.getPlotSchematicMinPoint().getY()) 347 .toVector3())) 348 .add(min.getBlockVector3().withY(hybridPlotWorld.SCHEM_Y).toVector3()); 349 result.setEntity(new PopulatingEntity( 350 entity, 351 new com.sk89q.worldedit.util.Location(NullWorld.getInstance(), pos) 352 )); 353 } 354 } 355 } catch (RegionOperationException e) { 356 throw new RuntimeException(e); 357 } 358 if (overlap) { 359 try { 360 region.shift(BlockVector3.at(-hybridPlotWorld.SIZE, 0, -hybridPlotWorld.SIZE)); 361 for (Entity entity : hybridPlotWorld.getPlotSchematicEntities()) { 362 if (region.contains(entity.getLocation().toVector().toBlockPoint())) { 363 result.setEntity(entity); 364 } 365 } 366 } catch (RegionOperationException e) { 367 throw new RuntimeException(e); 368 } 369 } 370 } 371 return; 372 } 373 374 @Override 375 public PlotArea getNewPlotArea(String world, String id, PlotId min, PlotId max) { 376 return this.hybridPlotWorldFactory.create(world, id, this, min, max); 377 } 378 379 @Override 380 public void initialize(PlotArea area) { 381 // All initialization is done in the PlotArea class 382 } 383 384 @Override 385 public BiomeType getBiome(final PlotArea settings, final int worldX, final int worldY, final int worldZ) { 386 HybridPlotWorld hybridPlotWorld = (HybridPlotWorld) settings; 387 if (!hybridPlotWorld.PLOT_SCHEMATIC && !hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 388 return hybridPlotWorld.getPlotBiome(); 389 } 390 int relativeX = worldX; 391 int relativeZ = worldZ; 392 if (hybridPlotWorld.ROAD_OFFSET_X != 0) { 393 relativeX -= hybridPlotWorld.ROAD_OFFSET_X; 394 } 395 if (hybridPlotWorld.ROAD_OFFSET_Z != 0) { 396 relativeZ -= hybridPlotWorld.ROAD_OFFSET_Z; 397 } 398 int size = hybridPlotWorld.PLOT_WIDTH + hybridPlotWorld.ROAD_WIDTH; 399 relativeX = Math.floorMod(relativeX, size); 400 relativeZ = Math.floorMod(relativeZ, size); 401 BiomeType biome = hybridPlotWorld.G_SCH_B.get(MathMan.pair((short) relativeX, (short) relativeZ)); 402 return biome == null ? hybridPlotWorld.getPlotBiome() : biome; 403 } 404 405 private enum SchematicFeature { 406 BIOMES, 407 ROAD, 408 POPULATING 409 } 410 411 /** 412 * Wrapper to allow a WorldEdit {@link Entity} to effectively have a mutable location as the location in its NBT should be changed 413 * when set to the world. 414 * 415 * @since 6.9.0 416 */ 417 private static final class PopulatingEntity implements Entity { 418 419 private final Entity parent; 420 private com.sk89q.worldedit.util.Location location; 421 422 /** 423 * @since 6.9.0 424 */ 425 private PopulatingEntity(Entity parent, com.sk89q.worldedit.util.Location location) { 426 this.parent = parent; 427 this.location = location; 428 } 429 430 @Nullable 431 @Override 432 public BaseEntity getState() { 433 return parent.getState(); 434 } 435 436 @Override 437 public boolean remove() { 438 return parent.remove(); 439 } 440 441 @Override 442 public com.sk89q.worldedit.util.Location getLocation() { 443 return location; 444 } 445 446 @Override 447 public boolean setLocation(final com.sk89q.worldedit.util.Location location) { 448 this.location = location; 449 return true; 450 } 451 452 @Override 453 public Extent getExtent() { 454 return parent.getExtent(); 455 } 456 457 @Nullable 458 @Override 459 public <T> T getFacet(final Class<? extends T> cls) { 460 return parent.getFacet(cls); 461 } 462 463 } 464 465}