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.ScopedQueueCoordinator; 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 045public class HybridGen extends IndependentPlotGenerator { 046 047 private static final CuboidRegion CHUNK = new CuboidRegion(BlockVector3.ZERO, BlockVector3.at(15, 396, 15)); 048 private final HybridPlotWorldFactory hybridPlotWorldFactory; 049 050 @Inject 051 public HybridGen(final @NonNull HybridPlotWorldFactory hybridPlotWorldFactory) { 052 this.hybridPlotWorldFactory = hybridPlotWorldFactory; 053 } 054 055 @Override 056 public String getName() { 057 return PlotSquared.platform().pluginName(); 058 } 059 060 private void placeSchem( 061 HybridPlotWorld world, 062 ScopedQueueCoordinator result, 063 short relativeX, 064 short relativeZ, 065 int x, 066 int z, 067 boolean isRoad, 068 boolean isPopulating 069 ) { 070 int minY; // Math.min(world.PLOT_HEIGHT, world.ROAD_HEIGHT); 071 if ((isRoad && Settings.Schematics.PASTE_ROAD_ON_TOP) || (!isRoad && Settings.Schematics.PASTE_ON_TOP)) { 072 minY = world.SCHEM_Y; 073 } else { 074 minY = world.getMinBuildHeight(); 075 } 076 BaseBlock[] blocks = world.G_SCH.get(MathMan.pair(relativeX, relativeZ)); 077 if (blocks != null) { 078 for (int y = 0; y < blocks.length; y++) { 079 if (blocks[y] != null) { 080 if (!isPopulating || blocks[y].hasNbtData()) { 081 result.setBlock(x, minY + y, z, blocks[y]); 082 } 083 } 084 } 085 } 086 BiomeType biome = world.G_SCH_B.get(MathMan.pair(relativeX, relativeZ)); 087 if (biome != null) { 088 result.setBiome(x, z, biome); 089 } 090 } 091 092 @Override 093 public void generateChunk(@NonNull ScopedQueueCoordinator result, @NonNull PlotArea settings) { 094 Preconditions.checkNotNull(result, "result cannot be null"); 095 Preconditions.checkNotNull(settings, "settings cannot be null"); 096 097 HybridPlotWorld hybridPlotWorld = (HybridPlotWorld) settings; 098 // Biome 099 result.fillBiome(hybridPlotWorld.getPlotBiome()); 100 // Bedrock 101 if (hybridPlotWorld.PLOT_BEDROCK) { 102 for (short x = 0; x < 16; x++) { 103 for (short z = 0; z < 16; z++) { 104 result.setBlock(x, hybridPlotWorld.getMinGenHeight(), z, BlockTypes.BEDROCK.getDefaultState()); 105 } 106 } 107 } 108 // Coords 109 Location min = result.getMin(); 110 int bx = min.getX() - hybridPlotWorld.ROAD_OFFSET_X; 111 int bz = min.getZ() - hybridPlotWorld.ROAD_OFFSET_Z; 112 // The relative X-coordinate (within the plot) of the minimum X coordinate 113 // contained in the scoped queue 114 short relativeOffsetX; 115 if (bx < 0) { 116 relativeOffsetX = (short) (hybridPlotWorld.SIZE + (bx % hybridPlotWorld.SIZE)); 117 } else { 118 relativeOffsetX = (short) (bx % hybridPlotWorld.SIZE); 119 } 120 // The relative Z-coordinate (within the plot) of the minimum Z coordinate 121 // contained in the scoped queue 122 short relativeOffsetZ; 123 if (bz < 0) { 124 relativeOffsetZ = (short) (hybridPlotWorld.SIZE + (bz % hybridPlotWorld.SIZE)); 125 } else { 126 relativeOffsetZ = (short) (bz % hybridPlotWorld.SIZE); 127 } 128 // The X-coordinate of a given X coordinate, relative to the 129 // plot (Counting from the corner with the least positive 130 // coordinates) 131 short[] relativeX = new short[16]; 132 boolean[] insideRoadX = new boolean[16]; 133 boolean[] insideWallX = new boolean[16]; 134 short offsetX = relativeOffsetX; 135 for (short i = 0; i < 16; i++) { 136 if (offsetX >= hybridPlotWorld.SIZE) { 137 offsetX -= hybridPlotWorld.SIZE; 138 } 139 relativeX[i] = offsetX; 140 if (hybridPlotWorld.ROAD_WIDTH != 0) { 141 insideRoadX[i] = offsetX < hybridPlotWorld.PATH_WIDTH_LOWER || offsetX > hybridPlotWorld.PATH_WIDTH_UPPER; 142 insideWallX[i] = offsetX == hybridPlotWorld.PATH_WIDTH_LOWER || offsetX == hybridPlotWorld.PATH_WIDTH_UPPER; 143 } 144 offsetX++; 145 } 146 // The Z-coordinate of a given Z coordinate, relative to the 147 // plot (Counting from the corner with the least positive 148 // coordinates) 149 short[] relativeZ = new short[16]; 150 boolean[] insideRoadZ = new boolean[16]; 151 boolean[] insideWallZ = new boolean[16]; 152 short offsetZ = relativeOffsetZ; 153 for (short i = 0; i < 16; i++) { 154 if (offsetZ >= hybridPlotWorld.SIZE) { 155 offsetZ -= hybridPlotWorld.SIZE; 156 } 157 relativeZ[i] = offsetZ; 158 if (hybridPlotWorld.ROAD_WIDTH != 0) { 159 insideRoadZ[i] = offsetZ < hybridPlotWorld.PATH_WIDTH_LOWER || offsetZ > hybridPlotWorld.PATH_WIDTH_UPPER; 160 insideWallZ[i] = offsetZ == hybridPlotWorld.PATH_WIDTH_LOWER || offsetZ == hybridPlotWorld.PATH_WIDTH_UPPER; 161 } 162 offsetZ++; 163 } 164 // generation 165 int startY = hybridPlotWorld.getMinGenHeight() + (hybridPlotWorld.PLOT_BEDROCK ? 1 : 0); 166 for (short x = 0; x < 16; x++) { 167 if (insideRoadX[x]) { 168 for (short z = 0; z < 16; z++) { 169 // Road 170 for (int y = startY; y <= hybridPlotWorld.ROAD_HEIGHT; y++) { 171 result.setBlock(x, y, z, hybridPlotWorld.ROAD_BLOCK.toPattern()); 172 } 173 if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 174 placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, false); 175 } 176 } 177 } else if (insideWallX[x]) { 178 for (short z = 0; z < 16; z++) { 179 if (insideRoadZ[z]) { 180 // road 181 for (int y = startY; y <= hybridPlotWorld.ROAD_HEIGHT; y++) { 182 result.setBlock(x, y, z, hybridPlotWorld.ROAD_BLOCK.toPattern()); 183 } 184 if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 185 placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, false); 186 } 187 } else { 188 // wall 189 for (int y = startY; y <= hybridPlotWorld.WALL_HEIGHT; y++) { 190 result.setBlock(x, y, z, hybridPlotWorld.WALL_FILLING.toPattern()); 191 } 192 if (!hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 193 if (hybridPlotWorld.PLACE_TOP_BLOCK) { 194 result.setBlock(x, hybridPlotWorld.WALL_HEIGHT + 1, z, hybridPlotWorld.WALL_BLOCK.toPattern()); 195 } 196 } else { 197 placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, false); 198 } 199 } 200 } 201 } else { 202 for (short z = 0; z < 16; z++) { 203 if (insideRoadZ[z]) { 204 // road 205 for (int y = startY; y <= hybridPlotWorld.ROAD_HEIGHT; y++) { 206 result.setBlock(x, y, z, hybridPlotWorld.ROAD_BLOCK.toPattern()); 207 } 208 if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 209 placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, false); 210 } 211 } else if (insideWallZ[z]) { 212 // wall 213 for (int y = startY; y <= hybridPlotWorld.WALL_HEIGHT; y++) { 214 result.setBlock(x, y, z, hybridPlotWorld.WALL_FILLING.toPattern()); 215 } 216 if (!hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 217 if (hybridPlotWorld.PLACE_TOP_BLOCK) { 218 result.setBlock(x, hybridPlotWorld.WALL_HEIGHT + 1, z, hybridPlotWorld.WALL_BLOCK.toPattern()); 219 } 220 } else { 221 placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, false); 222 } 223 } else { 224 // plot 225 for (int y = startY; y < hybridPlotWorld.PLOT_HEIGHT; y++) { 226 result.setBlock(x, y, z, hybridPlotWorld.MAIN_BLOCK.toPattern()); 227 } 228 result.setBlock(x, hybridPlotWorld.PLOT_HEIGHT, z, hybridPlotWorld.TOP_BLOCK.toPattern()); 229 if (hybridPlotWorld.PLOT_SCHEMATIC) { 230 placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, false, false); 231 } 232 } 233 } 234 } 235 } 236 } 237 238 @Override 239 public boolean populateChunk(final ScopedQueueCoordinator result, final PlotArea settings) { 240 HybridPlotWorld hybridPlotWorld = (HybridPlotWorld) settings; 241 if (!hybridPlotWorld.populationNeeded()) { 242 return false; 243 } 244 // Coords 245 Location min = result.getMin(); 246 int bx = min.getX() - hybridPlotWorld.ROAD_OFFSET_X; 247 int bz = min.getZ() - hybridPlotWorld.ROAD_OFFSET_Z; 248 // The relative X-coordinate (within the plot) of the minimum X coordinate 249 // contained in the scoped queue 250 short relativeOffsetX; 251 if (bx < 0) { 252 relativeOffsetX = (short) (hybridPlotWorld.SIZE + (bx % hybridPlotWorld.SIZE)); 253 } else { 254 relativeOffsetX = (short) (bx % hybridPlotWorld.SIZE); 255 } 256 // The relative Z-coordinate (within the plot) of the minimum Z coordinate 257 // contained in the scoped queue 258 short relativeOffsetZ; 259 if (bz < 0) { 260 relativeOffsetZ = (short) (hybridPlotWorld.SIZE + (bz % hybridPlotWorld.SIZE)); 261 } else { 262 relativeOffsetZ = (short) (bz % hybridPlotWorld.SIZE); 263 } 264 boolean allRoad = true; 265 boolean overlap = false; 266 267 // The X-coordinate of a given X coordinate, relative to the 268 // plot (Counting from the corner with the least positive 269 // coordinates) 270 short[] relativeX = new short[16]; 271 boolean[] insideRoadX = new boolean[16]; 272 boolean[] insideWallX = new boolean[16]; 273 short offsetX = relativeOffsetX; 274 for (short i = 0; i < 16; i++) { 275 if (offsetX >= hybridPlotWorld.SIZE) { 276 offsetX -= hybridPlotWorld.SIZE; 277 overlap = true; 278 } 279 relativeX[i] = offsetX; 280 if (hybridPlotWorld.ROAD_WIDTH != 0) { 281 boolean insideRoad = offsetX < hybridPlotWorld.PATH_WIDTH_LOWER || offsetX > hybridPlotWorld.PATH_WIDTH_UPPER; 282 boolean insideWall = offsetX == hybridPlotWorld.PATH_WIDTH_LOWER || offsetX == hybridPlotWorld.PATH_WIDTH_UPPER; 283 insideRoadX[i] = insideRoad; 284 insideWallX[i] = insideWall; 285 allRoad &= insideRoad && insideWall; 286 } 287 offsetX++; 288 } 289 290 // The Z-coordinate of a given Z coordinate, relative to the 291 // plot (Counting from the corner with the least positive 292 // coordinates) 293 short[] relativeZ = new short[16]; 294 boolean[] insideRoadZ = new boolean[16]; 295 boolean[] insideWallZ = new boolean[16]; 296 short offsetZ = relativeOffsetZ; 297 for (short i = 0; i < 16; i++) { 298 if (offsetZ >= hybridPlotWorld.SIZE) { 299 offsetZ -= hybridPlotWorld.SIZE; 300 overlap = true; 301 } 302 relativeZ[i] = offsetZ; 303 if (hybridPlotWorld.ROAD_WIDTH != 0) { 304 boolean insideRoad = offsetZ < hybridPlotWorld.PATH_WIDTH_LOWER || offsetZ > hybridPlotWorld.PATH_WIDTH_UPPER; 305 boolean insideWall = offsetZ == hybridPlotWorld.PATH_WIDTH_LOWER || offsetZ == hybridPlotWorld.PATH_WIDTH_UPPER; 306 insideRoadZ[i] = insideRoad; 307 insideWallZ[i] = insideWall; 308 allRoad &= insideRoad && insideWall; 309 } 310 offsetZ++; 311 } 312 for (short x = 0; x < 16; x++) { 313 if (insideRoadX[x] || insideWallX[x]) { 314 if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 315 for (short z = 0; z < 16; z++) { 316 placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, true); 317 } 318 } 319 } else { 320 for (short z = 0; z < 16; z++) { 321 if (insideRoadZ[z] || insideWallZ[z]) { 322 if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 323 placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, true, true); 324 } 325 } else if (hybridPlotWorld.PLOT_SCHEMATIC) { 326 placeSchem(hybridPlotWorld, result, relativeX[x], relativeZ[z], x, z, false, true); 327 } 328 } 329 } 330 } 331 if (!allRoad && hybridPlotWorld.getPlotSchematicEntities() != null && !hybridPlotWorld 332 .getPlotSchematicEntities() 333 .isEmpty()) { 334 CuboidRegion region = CHUNK.clone(); 335 try { 336 region.shift(hybridPlotWorld 337 .getPlotSchematicMinPoint() 338 .add(relativeOffsetX, 0, relativeOffsetZ) 339 .subtract(hybridPlotWorld.PATH_WIDTH_LOWER + 1, 0, hybridPlotWorld.PATH_WIDTH_LOWER + 1)); 340 for (Entity entity : hybridPlotWorld.getPlotSchematicEntities()) { 341 if (region.contains(entity.getLocation().toVector().toBlockPoint())) { 342 Vector3 pos = (entity.getLocation().toVector() 343 .subtract(region.getMinimumPoint().withY(hybridPlotWorld.getPlotSchematicMinPoint().getY()).toVector3())) 344 .add(min.getBlockVector3().withY(hybridPlotWorld.SCHEM_Y).toVector3()); 345 result.setEntity(new PopulatingEntity( 346 entity, 347 new com.sk89q.worldedit.util.Location(NullWorld.getInstance(), pos) 348 )); 349 } 350 } 351 } catch (RegionOperationException e) { 352 throw new RuntimeException(e); 353 } 354 if (overlap) { 355 try { 356 region.shift(BlockVector3.at(-hybridPlotWorld.SIZE, 0, -hybridPlotWorld.SIZE)); 357 for (Entity entity : hybridPlotWorld.getPlotSchematicEntities()) { 358 if (region.contains(entity.getLocation().toVector().toBlockPoint())) { 359 result.setEntity(entity); 360 } 361 } 362 } catch (RegionOperationException e) { 363 throw new RuntimeException(e); 364 } 365 } 366 } 367 return true; 368 } 369 370 @Override 371 public PlotArea getNewPlotArea(String world, String id, PlotId min, PlotId max) { 372 return this.hybridPlotWorldFactory.create(world, id, this, min, max); 373 } 374 375 @Override 376 public void initialize(PlotArea area) { 377 // All initialization is done in the PlotArea class 378 } 379 380 /** 381 * Wrapper to allow a WorldEdit {@link Entity} to effectively have a mutable location as the location in its NBT should be changed 382 * when set to the world. 383 * 384 * @since 6.9.0 385 */ 386 private static final class PopulatingEntity implements Entity { 387 388 private final Entity parent; 389 private com.sk89q.worldedit.util.Location location; 390 391 /** 392 * @since 6.9.0 393 */ 394 private PopulatingEntity(Entity parent, com.sk89q.worldedit.util.Location location) { 395 this.parent = parent; 396 this.location = location; 397 } 398 399 @Nullable 400 @Override 401 public BaseEntity getState() { 402 return parent.getState(); 403 } 404 405 @Override 406 public boolean remove() { 407 return parent.remove(); 408 } 409 410 @Override 411 public com.sk89q.worldedit.util.Location getLocation() { 412 return location; 413 } 414 415 @Override 416 public boolean setLocation(final com.sk89q.worldedit.util.Location location) { 417 this.location = location; 418 return true; 419 } 420 421 @Override 422 public Extent getExtent() { 423 return parent.getExtent(); 424 } 425 426 @Nullable 427 @Override 428 public <T> T getFacet(final Class<? extends T> cls) { 429 return parent.getFacet(cls); 430 } 431 432 } 433 434}