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.inject.Inject;
022import com.plotsquared.core.PlotSquared;
023import com.plotsquared.core.configuration.Settings;
024import com.plotsquared.core.inject.factory.ProgressSubscriberFactory;
025import com.plotsquared.core.location.Direction;
026import com.plotsquared.core.location.Location;
027import com.plotsquared.core.player.PlotPlayer;
028import com.plotsquared.core.plot.BlockBucket;
029import com.plotsquared.core.plot.Plot;
030import com.plotsquared.core.plot.PlotAreaTerrainType;
031import com.plotsquared.core.plot.PlotId;
032import com.plotsquared.core.queue.QueueCoordinator;
033import com.plotsquared.core.util.MathMan;
034import com.plotsquared.core.util.RegionManager;
035import com.plotsquared.core.util.task.TaskManager;
036import com.sk89q.worldedit.function.pattern.Pattern;
037import com.sk89q.worldedit.regions.CuboidRegion;
038import com.sk89q.worldedit.world.block.BlockTypes;
039import org.checkerframework.checker.nullness.qual.NonNull;
040import org.checkerframework.checker.nullness.qual.Nullable;
041
042import java.util.List;
043import java.util.Optional;
044
045/**
046 * A plot manager with square plots which tessellate on a square grid with the following sections: ROAD, WALL, BORDER (wall), PLOT, FLOOR (plot).
047 */
048public class ClassicPlotManager extends SquarePlotManager {
049
050    private final ClassicPlotWorld classicPlotWorld;
051    private final RegionManager regionManager;
052    private final ProgressSubscriberFactory subscriberFactory;
053
054    @Inject
055    public ClassicPlotManager(final @NonNull ClassicPlotWorld classicPlotWorld, final @NonNull RegionManager regionManager) {
056        super(classicPlotWorld, regionManager);
057        this.classicPlotWorld = classicPlotWorld;
058        this.regionManager = regionManager;
059        this.subscriberFactory = PlotSquared.platform().injector().getInstance(ProgressSubscriberFactory.class);
060    }
061
062    @Override
063    public boolean setComponent(
064            @NonNull PlotId plotId,
065            @NonNull String component,
066            @NonNull Pattern blocks,
067            @Nullable PlotPlayer<?> actor,
068            @Nullable QueueCoordinator queue
069    ) {
070        final Optional<ClassicPlotManagerComponent> componentOptional = ClassicPlotManagerComponent.fromString(component);
071        return componentOptional.map(classicPlotManagerComponent -> switch (classicPlotManagerComponent) {
072            case FLOOR -> setFloor(plotId, blocks, actor, queue);
073            case WALL -> setWallFilling(plotId, blocks, actor, queue);
074            case AIR -> setAir(plotId, blocks, actor, queue);
075            case MAIN -> setMain(plotId, blocks, actor, queue);
076            case MIDDLE -> setMiddle(plotId, blocks, queue);
077            case OUTLINE -> setOutline(plotId, blocks, actor, queue);
078            case BORDER -> setWall(plotId, blocks, actor, queue);
079            case ALL -> setAll(plotId, blocks, actor, queue);
080        }).orElse(false);
081    }
082
083    @Override
084    public boolean unClaimPlot(@NonNull Plot plot, @Nullable Runnable whenDone, @Nullable QueueCoordinator queue) {
085        setWallFilling(plot.getId(), classicPlotWorld.WALL_FILLING.toPattern(), null, queue);
086        if (classicPlotWorld.PLACE_TOP_BLOCK && (!classicPlotWorld.WALL_BLOCK.isAir() || !classicPlotWorld.WALL_BLOCK
087                .equals(classicPlotWorld.CLAIMED_WALL_BLOCK))) {
088            setWall(plot.getId(), classicPlotWorld.WALL_BLOCK.toPattern(), null, queue);
089        }
090        TaskManager.runTask(whenDone);
091        return true;
092    }
093
094    /**
095     * Set the plot floor
096     *
097     * @param plotId id of plot to set floor of
098     * @param blocks pattern to set
099     * @param queue  Nullable {@link QueueCoordinator}. If null, creates own queue and enqueues,
100     *               otherwise writes to the queue but does not enqueue.
101     * @return success or not
102     */
103    public boolean setFloor(
104            @NonNull PlotId plotId,
105            @NonNull Pattern blocks,
106            @Nullable PlotPlayer<?> actor,
107            @Nullable QueueCoordinator queue
108    ) {
109        Plot plot = classicPlotWorld.getPlotAbs(plotId);
110        if (plot != null && plot.isBasePlot()) {
111            return this.regionManager
112                    .setCuboids(
113                            classicPlotWorld,
114                            plot.getRegions(),
115                            blocks,
116                            classicPlotWorld.PLOT_HEIGHT,
117                            classicPlotWorld.PLOT_HEIGHT,
118                            actor,
119                            queue
120                    );
121        }
122        return false;
123    }
124
125    /**
126     * Sets the plot main, floor and air areas.
127     *
128     * @param plotId id of plot to set all of
129     * @param blocks pattern to set
130     * @param queue  Nullable {@link QueueCoordinator}. If null, creates own queue and enqueues,
131     *               otherwise writes to the queue but does not enqueue.
132     * @return success or not
133     */
134    public boolean setAll(
135            @NonNull PlotId plotId,
136            @NonNull Pattern blocks,
137            @Nullable PlotPlayer<?> actor,
138            @Nullable QueueCoordinator queue
139    ) {
140        Plot plot = classicPlotWorld.getPlotAbs(plotId);
141        if (plot != null && plot.isBasePlot()) {
142            return this.regionManager.setCuboids(
143                    classicPlotWorld,
144                    plot.getRegions(),
145                    blocks,
146                    classicPlotWorld.getMinBuildHeight(),
147                    classicPlotWorld.getMaxBuildHeight() - 1,
148                    actor,
149                    queue
150            );
151        }
152        return false;
153    }
154
155    /**
156     * Sets the plot air region.
157     *
158     * @param plotId id of plot to set air of
159     * @param blocks pattern to set
160     * @param queue  Nullable {@link QueueCoordinator}. If null, creates own queue and enqueues,
161     *               otherwise writes to the queue but does not enqueue.
162     * @return success or not
163     */
164    public boolean setAir(
165            @NonNull PlotId plotId,
166            @NonNull Pattern blocks,
167            @Nullable PlotPlayer<?> actor,
168            @Nullable QueueCoordinator queue
169    ) {
170        Plot plot = classicPlotWorld.getPlotAbs(plotId);
171        if (plot != null && plot.isBasePlot()) {
172            return this.regionManager
173                    .setCuboids(
174                            classicPlotWorld,
175                            plot.getRegions(),
176                            blocks,
177                            classicPlotWorld.PLOT_HEIGHT + 1,
178                            classicPlotWorld.getMaxBuildHeight() - 1,
179                            actor,
180                            queue
181                    );
182        }
183        return false;
184    }
185
186    /**
187     * Sets the plot main blocks.
188     *
189     * @param plotId id of plot to set main of
190     * @param blocks pattern to set
191     * @param queue  Nullable {@link QueueCoordinator}. If null, creates own queue and enqueues,
192     *               otherwise writes to the queue but does not enqueue.
193     * @return success or not
194     */
195    public boolean setMain(
196            @NonNull PlotId plotId,
197            @NonNull Pattern blocks,
198            @Nullable PlotPlayer<?> actor,
199            @Nullable QueueCoordinator queue
200    ) {
201        Plot plot = classicPlotWorld.getPlotAbs(plotId);
202        if (plot == null || plot.isBasePlot()) {
203            return this.regionManager.setCuboids(
204                    classicPlotWorld,
205                    plot.getRegions(),
206                    blocks,
207                    classicPlotWorld.getMinBuildHeight(),
208                    classicPlotWorld.PLOT_HEIGHT - 1,
209                    actor,
210                    queue
211            );
212        }
213        return false;
214    }
215
216    /**
217     * Set the middle plot block to a Pattern
218     *
219     * @param plotId id of plot to set middle block of
220     * @param blocks pattern to set
221     * @param queue  Nullable {@link QueueCoordinator}. If null, creates own queue and enqueues,
222     *               otherwise writes to the queue but does not enqueue.
223     * @return success or not
224     */
225    public boolean setMiddle(@NonNull PlotId plotId, @NonNull Pattern blocks, @Nullable QueueCoordinator queue) {
226        Plot plot = classicPlotWorld.getPlotAbs(plotId);
227        if (plot == null || !plot.isBasePlot()) {
228            return false;
229        }
230        Location[] corners = plot.getCorners();
231
232        boolean enqueue = false;
233        if (queue == null) {
234            queue = classicPlotWorld.getQueue();
235            enqueue = true;
236        }
237
238        int x = MathMan.average(corners[0].getX(), corners[1].getX());
239        int z = MathMan.average(corners[0].getZ(), corners[1].getZ());
240        queue.setBlock(x, classicPlotWorld.PLOT_HEIGHT, z, blocks);
241        return !enqueue || queue.enqueue();
242    }
243
244    /**
245     * Set a plot's outline
246     *
247     * @param plotId id of plot to set outline of
248     * @param blocks pattern to set
249     * @param queue  Nullable {@link QueueCoordinator}. If null, creates own queue and enqueues,
250     *               otherwise writes to the queue but does not enqueue.
251     * @return success or not
252     */
253    public boolean setOutline(
254            @NonNull PlotId plotId,
255            @NonNull Pattern blocks,
256            @Nullable PlotPlayer<?> actor,
257            @Nullable QueueCoordinator queue
258    ) {
259        if (classicPlotWorld.ROAD_WIDTH == 0) {
260            return false;
261        }
262        // When using full vanilla generation, don't generate the walls
263        if (classicPlotWorld.getTerrain() == PlotAreaTerrainType.ALL) {
264            // Return true because the method actually did what it's intended to in this case,
265            // which is absolutely nothing
266            return true;
267        }
268        Plot plot = classicPlotWorld.getPlotAbs(plotId);
269        if (plot == null) {
270            return false;
271        }
272        Location bottom = plot.getBottomAbs();
273        Location top = plot.getExtendedTopAbs();
274
275        boolean enqueue = false;
276        if (queue == null) {
277            queue = classicPlotWorld.getQueue();
278            enqueue = true;
279            if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) {
280                queue.addProgressSubscriber(subscriberFactory.createWithActor(actor));
281            }
282        }
283
284        int maxY = classicPlotWorld.getMaxBuildHeight() - 1;
285        if (!plot.isMerged(Direction.NORTH)) {
286            int z = bottom.getZ();
287            for (int x = bottom.getX(); x <= top.getX(); x++) {
288                for (int y = classicPlotWorld.PLOT_HEIGHT; y <= maxY; y++) {
289                    queue.setBlock(x, y, z, blocks);
290                }
291            }
292        }
293        if (!plot.isMerged(Direction.WEST)) {
294            int x = bottom.getX();
295            for (int z = bottom.getZ(); z <= top.getZ(); z++) {
296                for (int y = classicPlotWorld.PLOT_HEIGHT; y <= maxY; y++) {
297                    queue.setBlock(x, y, z, blocks);
298                }
299            }
300        }
301
302        if (!plot.isMerged(Direction.SOUTH)) {
303            int z = top.getZ();
304            for (int x = bottom.getX(); x <= top.getX(); x++) {
305                for (int y = classicPlotWorld.PLOT_HEIGHT; y <= maxY; y++) {
306                    queue.setBlock(x, y, z, blocks);
307                }
308            }
309        }
310        if (!plot.isMerged(Direction.EAST)) {
311            int x = top.getX();
312            for (int z = bottom.getZ(); z <= top.getZ(); z++) {
313                for (int y = classicPlotWorld.PLOT_HEIGHT; y <= maxY; y++) {
314                    queue.setBlock(x, y, z, blocks);
315                }
316            }
317        }
318        if (plot.isBasePlot()) {
319            for (CuboidRegion region : plot.getRegions()) {
320                Location pos1 = Location.at(
321                        classicPlotWorld.getWorldName(),
322                        region.getMinimumPoint().getX(),
323                        maxY,
324                        region.getMinimumPoint().getZ()
325                );
326                Location pos2 = Location.at(
327                        classicPlotWorld.getWorldName(),
328                        region.getMaximumPoint().getX(),
329                        maxY,
330                        region.getMaximumPoint().getZ()
331                );
332                queue.setCuboid(pos1, pos2, blocks);
333            }
334        }
335        return !enqueue || queue.enqueue();
336    }
337
338    /**
339     * Set the wall filling for a plot
340     *
341     * @param plotId id of plot to set wall filling of
342     * @param blocks pattern to set
343     * @param queue  Nullable {@link QueueCoordinator}. If null, creates own queue and enqueues,
344     *               otherwise writes to the queue but does not enqueue.
345     * @return success or not
346     */
347    public boolean setWallFilling(
348            @NonNull PlotId plotId,
349            @NonNull Pattern blocks,
350            @Nullable PlotPlayer<?> actor,
351            @Nullable QueueCoordinator queue
352    ) {
353        if (classicPlotWorld.ROAD_WIDTH == 0) {
354            return false;
355        }
356        // When using full vanilla generation, don't generate the walls
357        if (classicPlotWorld.getTerrain() == PlotAreaTerrainType.ALL) {
358            // Return true because the method actually did what it's intended to in this case,
359            // which is absolutely nothing
360            return true;
361        }
362        Plot plot = classicPlotWorld.getPlotAbs(plotId);
363        if (plot == null) {
364            return false;
365        }
366        Location bot = plot.getExtendedBottomAbs().subtract(
367                plot.isMerged(Direction.WEST) ? 0 : 1,
368                0,
369                plot.isMerged(Direction.NORTH) ? 0 : 1
370        );
371        Location top = plot.getExtendedTopAbs().add(1, 0, 1);
372
373        boolean enqueue = false;
374        if (queue == null) {
375            queue = classicPlotWorld.getQueue();
376            enqueue = true;
377            if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) {
378                queue.addProgressSubscriber(subscriberFactory.createWithActor(actor));
379            }
380        }
381
382        if (!plot.isMerged(Direction.NORTH)) {
383            int z = bot.getZ();
384            for (int x = bot.getX(); x < top.getX(); x++) {
385                for (int y = classicPlotWorld.getMinBuildHeight(); y <= classicPlotWorld.WALL_HEIGHT; y++) {
386                    queue.setBlock(x, y, z, blocks);
387                }
388            }
389        }
390        if (!plot.isMerged(Direction.WEST)) {
391            int x = bot.getX();
392            for (int z = bot.getZ(); z < top.getZ(); z++) {
393                for (int y = classicPlotWorld.getMinBuildHeight(); y <= classicPlotWorld.WALL_HEIGHT; y++) {
394                    queue.setBlock(x, y, z, blocks);
395                }
396            }
397        }
398        if (!plot.isMerged(Direction.SOUTH)) {
399            int z = top.getZ();
400            for (int x = bot.getX(); x < top.getX() + (plot.isMerged(Direction.EAST) ? 0 : 1); x++) {
401                for (int y = classicPlotWorld.getMinBuildHeight(); y <= classicPlotWorld.WALL_HEIGHT; y++) {
402                    queue.setBlock(x, y, z, blocks);
403                }
404            }
405        }
406        if (!plot.isMerged(Direction.EAST)) {
407            int x = top.getX();
408            for (int z = bot.getZ(); z < top.getZ() + (plot.isMerged(Direction.SOUTH) ? 0 : 1); z++) {
409                for (int y = classicPlotWorld.getMinBuildHeight(); y <= classicPlotWorld.WALL_HEIGHT; y++) {
410                    queue.setBlock(x, y, z, blocks);
411                }
412            }
413        }
414        return !enqueue || queue.enqueue();
415    }
416
417    /**
418     * Set a plot's wall top block only
419     *
420     * @param plotId id of plot to set wall top block of
421     * @param blocks pattern to set
422     * @param queue  Nullable {@link QueueCoordinator}. If null, creates own queue and enqueues,
423     *               otherwise writes to the queue but does not enqueue.
424     * @return success or not
425     */
426    public boolean setWall(
427            @NonNull PlotId plotId,
428            @NonNull Pattern blocks,
429            @Nullable PlotPlayer<?> actor,
430            @Nullable QueueCoordinator queue
431    ) {
432        if (classicPlotWorld.ROAD_WIDTH == 0) {
433            return false;
434        }
435        // When using full vanilla generation, don't generate the walls
436        if (classicPlotWorld.getTerrain() == PlotAreaTerrainType.ALL) {
437            // Return true because the method actually did what it's intended to in this case,
438            // which is absolutely nothing
439            return true;
440        }
441        Plot plot = classicPlotWorld.getPlotAbs(plotId);
442        if (plot == null) {
443            return false;
444        }
445        Location bot = plot.getExtendedBottomAbs().subtract(
446                plot.isMerged(Direction.WEST) ? 0 : 1,
447                0,
448                plot.isMerged(Direction.NORTH) ? 0 : 1
449        );
450        Location top = plot.getExtendedTopAbs().add(1, 0, 1);
451
452        boolean enqueue = false;
453        if (queue == null) {
454            enqueue = true;
455            queue = classicPlotWorld.getQueue();
456            if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) {
457                queue.addProgressSubscriber(subscriberFactory.createWithActor(actor));
458            }
459        }
460
461        int y = classicPlotWorld.WALL_HEIGHT + 1;
462        if (!plot.isMerged(Direction.NORTH)) {
463            int z = bot.getZ();
464            for (int x = bot.getX(); x < top.getX(); x++) {
465                queue.setBlock(x, y, z, blocks);
466            }
467        }
468        if (!plot.isMerged(Direction.WEST)) {
469            int x = bot.getX();
470            for (int z = bot.getZ(); z < top.getZ(); z++) {
471                queue.setBlock(x, y, z, blocks);
472            }
473        }
474        if (!plot.isMerged(Direction.SOUTH)) {
475            int z = top.getZ();
476            for (int x = bot.getX(); x < top.getX() + (plot.isMerged(Direction.EAST) ? 0 : 1); x++) {
477                queue.setBlock(x, y, z, blocks);
478            }
479        }
480        if (!plot.isMerged(Direction.EAST)) {
481            int x = top.getX();
482            for (int z = bot.getZ(); z < top.getZ() + (plot.isMerged(Direction.SOUTH) ? 0 : 1); z++) {
483                queue.setBlock(x, y, z, blocks);
484            }
485        }
486        return !enqueue || queue.enqueue();
487    }
488
489    @Override
490    public boolean createRoadEast(@NonNull Plot plot, @Nullable QueueCoordinator queue) {
491        Location pos1 = getPlotBottomLocAbs(plot.getId());
492        Location pos2 = getPlotTopLocAbs(plot.getId());
493        int sx = pos2.getX() + 1;
494        int ex = sx + classicPlotWorld.ROAD_WIDTH - 1;
495        int sz = pos1.getZ() - 2;
496        int ez = pos2.getZ() + 2;
497
498        boolean enqueue = false;
499        if (queue == null) {
500            queue = classicPlotWorld.getQueue();
501            enqueue = true;
502        }
503
504        int maxY = classicPlotWorld.getMaxGenHeight();
505        queue.setCuboid(
506                Location.at(
507                        classicPlotWorld.getWorldName(),
508                        sx,
509                        classicPlotWorld.schematicStartHeight() + 1,
510                        sz + 1
511                ),
512                Location.at(classicPlotWorld.getWorldName(), ex, maxY, ez - 1), BlockTypes.AIR.getDefaultState()
513        );
514        if (classicPlotWorld.PLOT_BEDROCK) {
515            queue.setCuboid(
516                    Location.at(classicPlotWorld.getWorldName(), sx, classicPlotWorld.getMinGenHeight(), sz + 1),
517                    Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.getMinGenHeight(), ez - 1),
518                    BlockTypes.BEDROCK.getDefaultState()
519            );
520        }
521        int startYOffset = classicPlotWorld.PLOT_BEDROCK ? 1 : 0;
522        queue.setCuboid(
523                Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.getMinGenHeight() + startYOffset, sz + 1),
524                Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.WALL_HEIGHT, ez - 1),
525                classicPlotWorld.WALL_FILLING.toPattern()
526        );
527        queue.setCuboid(
528                Location.at(classicPlotWorld.getWorldName(), sx, classicPlotWorld.getMinGenHeight() + startYOffset, sz + 1),
529                Location.at(classicPlotWorld.getWorldName(), sx, classicPlotWorld.WALL_HEIGHT, ez - 1),
530                classicPlotWorld.WALL_FILLING.toPattern()
531        );
532        queue.setCuboid(
533                Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.getMinGenHeight() + startYOffset, sz + 1),
534                Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.ROAD_HEIGHT, ez - 1),
535                classicPlotWorld.ROAD_BLOCK.toPattern()
536        );
537
538        if (classicPlotWorld.PLACE_TOP_BLOCK) {
539            queue.setCuboid(
540                    Location.at(classicPlotWorld.getWorldName(), sx, classicPlotWorld.WALL_HEIGHT + 1, sz + 1),
541                    Location.at(classicPlotWorld.getWorldName(), sx, classicPlotWorld.WALL_HEIGHT + 1, ez - 1),
542                    classicPlotWorld.WALL_BLOCK.toPattern()
543            );
544            queue.setCuboid(
545                    Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.WALL_HEIGHT + 1, sz + 1),
546                    Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.WALL_HEIGHT + 1, ez - 1),
547                    classicPlotWorld.WALL_BLOCK.toPattern()
548            );
549        }
550        return !enqueue || queue.enqueue();
551    }
552
553    @Override
554    public boolean createRoadSouth(@NonNull Plot plot, @Nullable QueueCoordinator queue) {
555        Location pos1 = getPlotBottomLocAbs(plot.getId());
556        Location pos2 = getPlotTopLocAbs(plot.getId());
557        int sz = pos2.getZ() + 1;
558        int ez = sz + classicPlotWorld.ROAD_WIDTH - 1;
559        int sx = pos1.getX() - 2;
560        int ex = pos2.getX() + 2;
561
562        boolean enqueue = false;
563        if (queue == null) {
564            queue = classicPlotWorld.getQueue();
565            enqueue = true;
566        }
567
568        queue.setCuboid(
569                Location.at(
570                        classicPlotWorld.getWorldName(),
571                        sx + 1,
572                        classicPlotWorld.schematicStartHeight() + 1,
573                        sz
574                ),
575                Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.getMaxGenHeight(), ez),
576                BlockTypes.AIR.getDefaultState()
577        );
578        if (classicPlotWorld.PLOT_BEDROCK) {
579            queue.setCuboid(
580                    Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.getMinGenHeight(), sz),
581                    Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.getMinGenHeight(), ez),
582                    BlockTypes.BEDROCK.getDefaultState()
583            );
584        }
585        int startYOffset = classicPlotWorld.PLOT_BEDROCK ? 1 : 0;
586        queue.setCuboid(
587                Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.getMinGenHeight() + startYOffset, sz),
588                Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.WALL_HEIGHT, sz),
589                classicPlotWorld.WALL_FILLING.toPattern()
590        );
591        queue.setCuboid(
592                Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.getMinGenHeight() + startYOffset, ez),
593                Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.WALL_HEIGHT, ez),
594                classicPlotWorld.WALL_FILLING.toPattern()
595        );
596        queue.setCuboid(
597                Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.getMinGenHeight() + startYOffset, sz + 1),
598                Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.ROAD_HEIGHT, ez - 1),
599                classicPlotWorld.ROAD_BLOCK.toPattern()
600        );
601
602        if (classicPlotWorld.PLACE_TOP_BLOCK) {
603            queue.setCuboid(
604                    Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.WALL_HEIGHT + 1, sz),
605                    Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.WALL_HEIGHT + 1, sz),
606                    classicPlotWorld.WALL_BLOCK.toPattern()
607            );
608            queue.setCuboid(
609                    Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.WALL_HEIGHT + 1, ez),
610                    Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.WALL_HEIGHT + 1, ez),
611                    classicPlotWorld.WALL_BLOCK.toPattern()
612            );
613        }
614        return !enqueue || queue.enqueue();
615    }
616
617
618    @Override
619    public boolean createRoadSouthEast(@NonNull Plot plot, @Nullable QueueCoordinator queue) {
620        Location pos2 = getPlotTopLocAbs(plot.getId());
621        int sx = pos2.getX() + 1;
622        int ex = sx + classicPlotWorld.ROAD_WIDTH - 1;
623        int sz = pos2.getZ() + 1;
624        int ez = sz + classicPlotWorld.ROAD_WIDTH - 1;
625
626        boolean enqueue = false;
627        if (queue == null) {
628            queue = classicPlotWorld.getQueue();
629            enqueue = true;
630        }
631
632        queue.setCuboid(
633                Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.ROAD_HEIGHT + 1, sz + 1),
634                Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.getMaxGenHeight(), ez - 1),
635                BlockTypes.AIR.getDefaultState()
636        );
637        if (classicPlotWorld.PLOT_BEDROCK) {
638            queue.setCuboid(
639                    Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.getMinGenHeight(), sz + 1),
640                    Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.getMinGenHeight(), ez - 1),
641                    BlockTypes.BEDROCK.getDefaultState()
642            );
643        }
644        int startYOffset = classicPlotWorld.PLOT_BEDROCK ? 1 : 0;
645        queue.setCuboid(
646                Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.getMinGenHeight() + startYOffset, sz + 1),
647                Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.ROAD_HEIGHT, ez - 1),
648                classicPlotWorld.ROAD_BLOCK.toPattern()
649        );
650        return !enqueue || queue.enqueue();
651    }
652
653    @Override
654    public boolean removeRoadEast(@NonNull Plot plot, @Nullable QueueCoordinator queue) {
655        Location pos1 = getPlotBottomLocAbs(plot.getId());
656        Location pos2 = getPlotTopLocAbs(plot.getId());
657        int sx = pos2.getX() + 1;
658        int ex = sx + classicPlotWorld.ROAD_WIDTH - 1;
659        int sz = pos1.getZ() - 1;
660        int ez = pos2.getZ() + 1;
661
662        boolean enqueue = false;
663        if (queue == null) {
664            queue = classicPlotWorld.getQueue();
665            enqueue = true;
666        }
667
668        queue
669                .setCuboid(
670                        Location.at(
671                                classicPlotWorld.getWorldName(),
672                                sx,
673                                classicPlotWorld.schematicStartHeight() + 1,
674                                sz
675                        ),
676                        Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.getMaxGenHeight(), ez),
677                        BlockTypes.AIR.getDefaultState()
678                );
679        int startYOffset = classicPlotWorld.PLOT_BEDROCK ? 1 : 0;
680        queue.setCuboid(
681                Location.at(classicPlotWorld.getWorldName(), sx, classicPlotWorld.getMinGenHeight() + startYOffset, sz + 1),
682                Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.PLOT_HEIGHT - 1, ez - 1),
683                classicPlotWorld.MAIN_BLOCK.toPattern()
684        );
685        queue.setCuboid(
686                Location.at(classicPlotWorld.getWorldName(), sx, classicPlotWorld.PLOT_HEIGHT, sz + 1),
687                Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.PLOT_HEIGHT, ez - 1),
688                classicPlotWorld.TOP_BLOCK.toPattern()
689        );
690
691        return !enqueue || queue.enqueue();
692    }
693
694    @Override
695    public boolean removeRoadSouth(@NonNull Plot plot, @Nullable QueueCoordinator queue) {
696        Location pos1 = getPlotBottomLocAbs(plot.getId());
697        Location pos2 = getPlotTopLocAbs(plot.getId());
698        int sz = pos2.getZ() + 1;
699        int ez = sz + classicPlotWorld.ROAD_WIDTH - 1;
700        int sx = pos1.getX() - 1;
701        int ex = pos2.getX() + 1;
702
703        boolean enqueue = false;
704        if (queue == null) {
705            queue = classicPlotWorld.getQueue();
706            enqueue = true;
707        }
708
709        queue
710                .setCuboid(
711                        Location.at(
712                                classicPlotWorld.getWorldName(),
713                                sx,
714                                classicPlotWorld.schematicStartHeight() + 1,
715                                sz
716                        ),
717                        Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.getMaxGenHeight(), ez),
718                        BlockTypes.AIR.getDefaultState()
719                );
720        int startYOffset = classicPlotWorld.PLOT_BEDROCK ? 1 : 0;
721        queue.setCuboid(
722                Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.getMinGenHeight() + startYOffset, sz),
723                Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.PLOT_HEIGHT - 1, ez),
724                classicPlotWorld.MAIN_BLOCK.toPattern()
725        );
726        queue.setCuboid(
727                Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.PLOT_HEIGHT, sz),
728                Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.PLOT_HEIGHT, ez),
729                classicPlotWorld.TOP_BLOCK.toPattern()
730        );
731
732        return !enqueue || queue.enqueue();
733    }
734
735    @Override
736    public boolean removeRoadSouthEast(@NonNull Plot plot, @Nullable QueueCoordinator queue) {
737        Location location = getPlotTopLocAbs(plot.getId());
738        int sx = location.getX() + 1;
739        int ex = sx + classicPlotWorld.ROAD_WIDTH - 1;
740        int sz = location.getZ() + 1;
741        int ez = sz + classicPlotWorld.ROAD_WIDTH - 1;
742
743        boolean enqueue = false;
744        if (queue == null) {
745            queue = classicPlotWorld.getQueue();
746            enqueue = true;
747        }
748
749        queue
750                .setCuboid(
751                        Location.at(
752                                classicPlotWorld.getWorldName(),
753                                sx,
754                                classicPlotWorld.schematicStartHeight() + 1,
755                                sz
756                        ),
757                        Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.getMaxGenHeight(), ez),
758                        BlockTypes.AIR.getDefaultState()
759                );
760        int startYOffset = classicPlotWorld.PLOT_BEDROCK ? 1 : 0;
761        queue.setCuboid(
762                Location.at(classicPlotWorld.getWorldName(), sx, classicPlotWorld.getMinGenHeight() + startYOffset, sz),
763                Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.PLOT_HEIGHT - 1, ez),
764                classicPlotWorld.MAIN_BLOCK.toPattern()
765        );
766        queue.setCuboid(
767                Location.at(classicPlotWorld.getWorldName(), sx, classicPlotWorld.PLOT_HEIGHT, sz),
768                Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.PLOT_HEIGHT, ez),
769                classicPlotWorld.TOP_BLOCK.toPattern()
770        );
771
772        return !enqueue || queue.enqueue();
773    }
774
775    @Override
776    public boolean finishPlotMerge(@NonNull List<PlotId> plotIds, @Nullable QueueCoordinator queue) {
777        final BlockBucket claim = classicPlotWorld.CLAIMED_WALL_BLOCK;
778        if (classicPlotWorld.PLACE_TOP_BLOCK && (!claim.isAir() || !claim.equals(classicPlotWorld.WALL_BLOCK))) {
779            for (PlotId plotId : plotIds) {
780                setWall(plotId, claim.toPattern(), null, queue);
781            }
782        }
783        if (Settings.General.MERGE_REPLACE_WALL) {
784            final BlockBucket wallBlock = classicPlotWorld.WALL_FILLING;
785            for (PlotId id : plotIds) {
786                setWallFilling(id, wallBlock.toPattern(), null, queue);
787            }
788        }
789        return true;
790    }
791
792    @Override
793    public boolean finishPlotUnlink(@NonNull List<PlotId> plotIds, @Nullable QueueCoordinator queue) {
794        final BlockBucket claim = classicPlotWorld.CLAIMED_WALL_BLOCK;
795        if (classicPlotWorld.PLACE_TOP_BLOCK && (!claim.isAir() || !claim.equals(classicPlotWorld.WALL_BLOCK))) {
796            for (PlotId id : plotIds) {
797                setWall(id, claim.toPattern(), null, queue);
798            }
799        }
800        return true; // return false if unlink has been denied
801    }
802
803    @Override
804    public boolean startPlotMerge(@NonNull List<PlotId> plotIds, @Nullable QueueCoordinator queue) {
805        return true;
806    }
807
808    @Override
809    public boolean startPlotUnlink(@NonNull List<PlotId> plotIds, @Nullable QueueCoordinator queue) {
810        return true;
811    }
812
813    @Override
814    public boolean claimPlot(@NonNull Plot plot, @Nullable QueueCoordinator queue) {
815        final BlockBucket claim = classicPlotWorld.CLAIMED_WALL_BLOCK;
816        if (classicPlotWorld.PLACE_TOP_BLOCK && (!claim.isAir() || !claim.equals(classicPlotWorld.WALL_BLOCK))) {
817            return setWall(plot.getId(), claim.toPattern(), null, queue);
818        }
819        return true;
820    }
821
822    @Override
823    public String[] getPlotComponents(@NonNull PlotId plotId) {
824        return ClassicPlotManagerComponent.stringValues();
825    }
826
827    /**
828     * Retrieves the location of where a sign should be for a plot.
829     *
830     * @param plot The plot
831     * @return The location where a sign should be
832     */
833    @Override
834    public Location getSignLoc(@NonNull Plot plot) {
835        plot = plot.getBasePlot(false);
836        final Location bot = plot.getBottomAbs();
837        return Location.at(classicPlotWorld.getWorldName(), bot.getX() - 1, classicPlotWorld.ROAD_HEIGHT + 1, bot.getZ() - 2);
838    }
839
840}