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        int yStart = classicPlotWorld.getMinBuildHeight() + (classicPlotWorld.PLOT_BEDROCK ? 1 : 0);
383        if (!plot.isMerged(Direction.NORTH)) {
384            int z = bot.getZ();
385            for (int x = bot.getX(); x < top.getX(); x++) {
386                for (int y = yStart; y <= classicPlotWorld.WALL_HEIGHT; y++) {
387                    queue.setBlock(x, y, z, blocks);
388                }
389            }
390        }
391        if (!plot.isMerged(Direction.WEST)) {
392            int x = bot.getX();
393            for (int z = bot.getZ(); z < top.getZ(); z++) {
394                for (int y = yStart; y <= classicPlotWorld.WALL_HEIGHT; y++) {
395                    queue.setBlock(x, y, z, blocks);
396                }
397            }
398        }
399        if (!plot.isMerged(Direction.SOUTH)) {
400            int z = top.getZ();
401            for (int x = bot.getX(); x < top.getX() + (plot.isMerged(Direction.EAST) ? 0 : 1); x++) {
402                for (int y = yStart; y <= classicPlotWorld.WALL_HEIGHT; y++) {
403                    queue.setBlock(x, y, z, blocks);
404                }
405            }
406        }
407        if (!plot.isMerged(Direction.EAST)) {
408            int x = top.getX();
409            for (int z = bot.getZ(); z < top.getZ() + (plot.isMerged(Direction.SOUTH) ? 0 : 1); z++) {
410                for (int y = yStart; y <= classicPlotWorld.WALL_HEIGHT; y++) {
411                    queue.setBlock(x, y, z, blocks);
412                }
413            }
414        }
415        return !enqueue || queue.enqueue();
416    }
417
418    /**
419     * Set a plot's wall top block only
420     *
421     * @param plotId id of plot to set wall top block of
422     * @param blocks pattern to set
423     * @param queue  Nullable {@link QueueCoordinator}. If null, creates own queue and enqueues,
424     *               otherwise writes to the queue but does not enqueue.
425     * @return success or not
426     */
427    public boolean setWall(
428            @NonNull PlotId plotId,
429            @NonNull Pattern blocks,
430            @Nullable PlotPlayer<?> actor,
431            @Nullable QueueCoordinator queue
432    ) {
433        if (classicPlotWorld.ROAD_WIDTH == 0) {
434            return false;
435        }
436        // When using full vanilla generation, don't generate the walls
437        if (classicPlotWorld.getTerrain() == PlotAreaTerrainType.ALL) {
438            // Return true because the method actually did what it's intended to in this case,
439            // which is absolutely nothing
440            return true;
441        }
442        Plot plot = classicPlotWorld.getPlotAbs(plotId);
443        if (plot == null) {
444            return false;
445        }
446        Location bot = plot.getExtendedBottomAbs().subtract(
447                plot.isMerged(Direction.WEST) ? 0 : 1,
448                0,
449                plot.isMerged(Direction.NORTH) ? 0 : 1
450        );
451        Location top = plot.getExtendedTopAbs().add(1, 0, 1);
452
453        boolean enqueue = false;
454        if (queue == null) {
455            enqueue = true;
456            queue = classicPlotWorld.getQueue();
457            if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) {
458                queue.addProgressSubscriber(subscriberFactory.createWithActor(actor));
459            }
460        }
461
462        int y = classicPlotWorld.WALL_HEIGHT + 1;
463        if (!plot.isMerged(Direction.NORTH)) {
464            int z = bot.getZ();
465            for (int x = bot.getX(); x < top.getX(); x++) {
466                queue.setBlock(x, y, z, blocks);
467            }
468        }
469        if (!plot.isMerged(Direction.WEST)) {
470            int x = bot.getX();
471            for (int z = bot.getZ(); z < top.getZ(); z++) {
472                queue.setBlock(x, y, z, blocks);
473            }
474        }
475        if (!plot.isMerged(Direction.SOUTH)) {
476            int z = top.getZ();
477            for (int x = bot.getX(); x < top.getX() + (plot.isMerged(Direction.EAST) ? 0 : 1); x++) {
478                queue.setBlock(x, y, z, blocks);
479            }
480        }
481        if (!plot.isMerged(Direction.EAST)) {
482            int x = top.getX();
483            for (int z = bot.getZ(); z < top.getZ() + (plot.isMerged(Direction.SOUTH) ? 0 : 1); z++) {
484                queue.setBlock(x, y, z, blocks);
485            }
486        }
487        return !enqueue || queue.enqueue();
488    }
489
490    @Override
491    public boolean createRoadEast(@NonNull Plot plot, @Nullable QueueCoordinator queue) {
492        Location pos1 = getPlotBottomLocAbs(plot.getId());
493        Location pos2 = getPlotTopLocAbs(plot.getId());
494        int sx = pos2.getX() + 1;
495        int ex = sx + classicPlotWorld.ROAD_WIDTH - 1;
496        int sz = pos1.getZ() - 2;
497        int ez = pos2.getZ() + 2;
498
499        boolean enqueue = false;
500        if (queue == null) {
501            queue = classicPlotWorld.getQueue();
502            enqueue = true;
503        }
504
505        int maxY = classicPlotWorld.getMaxGenHeight();
506        queue.setCuboid(
507                Location.at(
508                        classicPlotWorld.getWorldName(),
509                        sx,
510                        classicPlotWorld.schematicStartHeight() + 1,
511                        sz + 1
512                ),
513                Location.at(classicPlotWorld.getWorldName(), ex, maxY, ez - 1), BlockTypes.AIR.getDefaultState()
514        );
515        if (classicPlotWorld.PLOT_BEDROCK) {
516            queue.setCuboid(
517                    Location.at(classicPlotWorld.getWorldName(), sx, classicPlotWorld.getMinGenHeight(), sz + 1),
518                    Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.getMinGenHeight(), ez - 1),
519                    BlockTypes.BEDROCK.getDefaultState()
520            );
521        }
522        int startYOffset = classicPlotWorld.PLOT_BEDROCK ? 1 : 0;
523        queue.setCuboid(
524                Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.getMinGenHeight() + startYOffset, sz + 1),
525                Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.WALL_HEIGHT, ez - 1),
526                classicPlotWorld.WALL_FILLING.toPattern()
527        );
528        queue.setCuboid(
529                Location.at(classicPlotWorld.getWorldName(), sx, classicPlotWorld.getMinGenHeight() + startYOffset, sz + 1),
530                Location.at(classicPlotWorld.getWorldName(), sx, classicPlotWorld.WALL_HEIGHT, ez - 1),
531                classicPlotWorld.WALL_FILLING.toPattern()
532        );
533        queue.setCuboid(
534                Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.getMinGenHeight() + startYOffset, sz + 1),
535                Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.ROAD_HEIGHT, ez - 1),
536                classicPlotWorld.ROAD_BLOCK.toPattern()
537        );
538
539        if (classicPlotWorld.PLACE_TOP_BLOCK) {
540            queue.setCuboid(
541                    Location.at(classicPlotWorld.getWorldName(), sx, classicPlotWorld.WALL_HEIGHT + 1, sz + 1),
542                    Location.at(classicPlotWorld.getWorldName(), sx, classicPlotWorld.WALL_HEIGHT + 1, ez - 1),
543                    classicPlotWorld.WALL_BLOCK.toPattern()
544            );
545            queue.setCuboid(
546                    Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.WALL_HEIGHT + 1, sz + 1),
547                    Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.WALL_HEIGHT + 1, ez - 1),
548                    classicPlotWorld.WALL_BLOCK.toPattern()
549            );
550        }
551        return !enqueue || queue.enqueue();
552    }
553
554    @Override
555    public boolean createRoadSouth(@NonNull Plot plot, @Nullable QueueCoordinator queue) {
556        Location pos1 = getPlotBottomLocAbs(plot.getId());
557        Location pos2 = getPlotTopLocAbs(plot.getId());
558        int sz = pos2.getZ() + 1;
559        int ez = sz + classicPlotWorld.ROAD_WIDTH - 1;
560        int sx = pos1.getX() - 2;
561        int ex = pos2.getX() + 2;
562
563        boolean enqueue = false;
564        if (queue == null) {
565            queue = classicPlotWorld.getQueue();
566            enqueue = true;
567        }
568
569        queue.setCuboid(
570                Location.at(
571                        classicPlotWorld.getWorldName(),
572                        sx + 1,
573                        classicPlotWorld.schematicStartHeight() + 1,
574                        sz
575                ),
576                Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.getMaxGenHeight(), ez),
577                BlockTypes.AIR.getDefaultState()
578        );
579        if (classicPlotWorld.PLOT_BEDROCK) {
580            queue.setCuboid(
581                    Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.getMinGenHeight(), sz),
582                    Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.getMinGenHeight(), ez),
583                    BlockTypes.BEDROCK.getDefaultState()
584            );
585        }
586        int startYOffset = classicPlotWorld.PLOT_BEDROCK ? 1 : 0;
587        queue.setCuboid(
588                Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.getMinGenHeight() + startYOffset, sz),
589                Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.WALL_HEIGHT, sz),
590                classicPlotWorld.WALL_FILLING.toPattern()
591        );
592        queue.setCuboid(
593                Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.getMinGenHeight() + startYOffset, ez),
594                Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.WALL_HEIGHT, ez),
595                classicPlotWorld.WALL_FILLING.toPattern()
596        );
597        queue.setCuboid(
598                Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.getMinGenHeight() + startYOffset, sz + 1),
599                Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.ROAD_HEIGHT, ez - 1),
600                classicPlotWorld.ROAD_BLOCK.toPattern()
601        );
602
603        if (classicPlotWorld.PLACE_TOP_BLOCK) {
604            queue.setCuboid(
605                    Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.WALL_HEIGHT + 1, sz),
606                    Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.WALL_HEIGHT + 1, sz),
607                    classicPlotWorld.WALL_BLOCK.toPattern()
608            );
609            queue.setCuboid(
610                    Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.WALL_HEIGHT + 1, ez),
611                    Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.WALL_HEIGHT + 1, ez),
612                    classicPlotWorld.WALL_BLOCK.toPattern()
613            );
614        }
615        return !enqueue || queue.enqueue();
616    }
617
618
619    @Override
620    public boolean createRoadSouthEast(@NonNull Plot plot, @Nullable QueueCoordinator queue) {
621        Location pos2 = getPlotTopLocAbs(plot.getId());
622        int sx = pos2.getX() + 1;
623        int ex = sx + classicPlotWorld.ROAD_WIDTH - 1;
624        int sz = pos2.getZ() + 1;
625        int ez = sz + classicPlotWorld.ROAD_WIDTH - 1;
626
627        boolean enqueue = false;
628        if (queue == null) {
629            queue = classicPlotWorld.getQueue();
630            enqueue = true;
631        }
632
633        queue.setCuboid(
634                Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.ROAD_HEIGHT + 1, sz + 1),
635                Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.getMaxGenHeight(), ez - 1),
636                BlockTypes.AIR.getDefaultState()
637        );
638        if (classicPlotWorld.PLOT_BEDROCK) {
639            queue.setCuboid(
640                    Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.getMinGenHeight(), sz + 1),
641                    Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.getMinGenHeight(), ez - 1),
642                    BlockTypes.BEDROCK.getDefaultState()
643            );
644        }
645        int startYOffset = classicPlotWorld.PLOT_BEDROCK ? 1 : 0;
646        queue.setCuboid(
647                Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.getMinGenHeight() + startYOffset, sz + 1),
648                Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.ROAD_HEIGHT, ez - 1),
649                classicPlotWorld.ROAD_BLOCK.toPattern()
650        );
651        return !enqueue || queue.enqueue();
652    }
653
654    @Override
655    public boolean removeRoadEast(@NonNull Plot plot, @Nullable QueueCoordinator queue) {
656        Location pos1 = getPlotBottomLocAbs(plot.getId());
657        Location pos2 = getPlotTopLocAbs(plot.getId());
658        int sx = pos2.getX() + 1;
659        int ex = sx + classicPlotWorld.ROAD_WIDTH - 1;
660        int sz = pos1.getZ() - 1;
661        int ez = pos2.getZ() + 1;
662
663        boolean enqueue = false;
664        if (queue == null) {
665            queue = classicPlotWorld.getQueue();
666            enqueue = true;
667        }
668
669        queue
670                .setCuboid(
671                        Location.at(
672                                classicPlotWorld.getWorldName(),
673                                sx,
674                                classicPlotWorld.schematicStartHeight() + 1,
675                                sz
676                        ),
677                        Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.getMaxGenHeight(), ez),
678                        BlockTypes.AIR.getDefaultState()
679                );
680        int startYOffset = classicPlotWorld.PLOT_BEDROCK ? 1 : 0;
681        queue.setCuboid(
682                Location.at(classicPlotWorld.getWorldName(), sx, classicPlotWorld.getMinGenHeight() + startYOffset, sz + 1),
683                Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.PLOT_HEIGHT - 1, ez - 1),
684                classicPlotWorld.MAIN_BLOCK.toPattern()
685        );
686        queue.setCuboid(
687                Location.at(classicPlotWorld.getWorldName(), sx, classicPlotWorld.PLOT_HEIGHT, sz + 1),
688                Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.PLOT_HEIGHT, ez - 1),
689                classicPlotWorld.TOP_BLOCK.toPattern()
690        );
691
692        return !enqueue || queue.enqueue();
693    }
694
695    @Override
696    public boolean removeRoadSouth(@NonNull Plot plot, @Nullable QueueCoordinator queue) {
697        Location pos1 = getPlotBottomLocAbs(plot.getId());
698        Location pos2 = getPlotTopLocAbs(plot.getId());
699        int sz = pos2.getZ() + 1;
700        int ez = sz + classicPlotWorld.ROAD_WIDTH - 1;
701        int sx = pos1.getX() - 1;
702        int ex = pos2.getX() + 1;
703
704        boolean enqueue = false;
705        if (queue == null) {
706            queue = classicPlotWorld.getQueue();
707            enqueue = true;
708        }
709
710        queue
711                .setCuboid(
712                        Location.at(
713                                classicPlotWorld.getWorldName(),
714                                sx,
715                                classicPlotWorld.schematicStartHeight() + 1,
716                                sz
717                        ),
718                        Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.getMaxGenHeight(), ez),
719                        BlockTypes.AIR.getDefaultState()
720                );
721        int startYOffset = classicPlotWorld.PLOT_BEDROCK ? 1 : 0;
722        queue.setCuboid(
723                Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.getMinGenHeight() + startYOffset, sz),
724                Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.PLOT_HEIGHT - 1, ez),
725                classicPlotWorld.MAIN_BLOCK.toPattern()
726        );
727        queue.setCuboid(
728                Location.at(classicPlotWorld.getWorldName(), sx + 1, classicPlotWorld.PLOT_HEIGHT, sz),
729                Location.at(classicPlotWorld.getWorldName(), ex - 1, classicPlotWorld.PLOT_HEIGHT, ez),
730                classicPlotWorld.TOP_BLOCK.toPattern()
731        );
732
733        return !enqueue || queue.enqueue();
734    }
735
736    @Override
737    public boolean removeRoadSouthEast(@NonNull Plot plot, @Nullable QueueCoordinator queue) {
738        Location location = getPlotTopLocAbs(plot.getId());
739        int sx = location.getX() + 1;
740        int ex = sx + classicPlotWorld.ROAD_WIDTH - 1;
741        int sz = location.getZ() + 1;
742        int ez = sz + classicPlotWorld.ROAD_WIDTH - 1;
743
744        boolean enqueue = false;
745        if (queue == null) {
746            queue = classicPlotWorld.getQueue();
747            enqueue = true;
748        }
749
750        queue
751                .setCuboid(
752                        Location.at(
753                                classicPlotWorld.getWorldName(),
754                                sx,
755                                classicPlotWorld.schematicStartHeight() + 1,
756                                sz
757                        ),
758                        Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.getMaxGenHeight(), ez),
759                        BlockTypes.AIR.getDefaultState()
760                );
761        int startYOffset = classicPlotWorld.PLOT_BEDROCK ? 1 : 0;
762        queue.setCuboid(
763                Location.at(classicPlotWorld.getWorldName(), sx, classicPlotWorld.getMinGenHeight() + startYOffset, sz),
764                Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.PLOT_HEIGHT - 1, ez),
765                classicPlotWorld.MAIN_BLOCK.toPattern()
766        );
767        queue.setCuboid(
768                Location.at(classicPlotWorld.getWorldName(), sx, classicPlotWorld.PLOT_HEIGHT, sz),
769                Location.at(classicPlotWorld.getWorldName(), ex, classicPlotWorld.PLOT_HEIGHT, ez),
770                classicPlotWorld.TOP_BLOCK.toPattern()
771        );
772
773        return !enqueue || queue.enqueue();
774    }
775
776    @Override
777    public boolean finishPlotMerge(@NonNull List<PlotId> plotIds, @Nullable QueueCoordinator queue) {
778        final BlockBucket claim = classicPlotWorld.CLAIMED_WALL_BLOCK;
779        if (classicPlotWorld.PLACE_TOP_BLOCK && (!claim.isAir() || !claim.equals(classicPlotWorld.WALL_BLOCK))) {
780            for (PlotId plotId : plotIds) {
781                setWall(plotId, claim.toPattern(), null, queue);
782            }
783        }
784        if (Settings.General.MERGE_REPLACE_WALL) {
785            final BlockBucket wallBlock = classicPlotWorld.WALL_FILLING;
786            for (PlotId id : plotIds) {
787                setWallFilling(id, wallBlock.toPattern(), null, queue);
788            }
789        }
790        return true;
791    }
792
793    @Override
794    public boolean finishPlotUnlink(@NonNull List<PlotId> plotIds, @Nullable QueueCoordinator queue) {
795        final BlockBucket claim = classicPlotWorld.CLAIMED_WALL_BLOCK;
796        if (classicPlotWorld.PLACE_TOP_BLOCK && (!claim.isAir() || !claim.equals(classicPlotWorld.WALL_BLOCK))) {
797            for (PlotId id : plotIds) {
798                setWall(id, claim.toPattern(), null, queue);
799            }
800        }
801        return true; // return false if unlink has been denied
802    }
803
804    @Override
805    public boolean startPlotMerge(@NonNull List<PlotId> plotIds, @Nullable QueueCoordinator queue) {
806        return true;
807    }
808
809    @Override
810    public boolean startPlotUnlink(@NonNull List<PlotId> plotIds, @Nullable QueueCoordinator queue) {
811        return true;
812    }
813
814    @Override
815    public boolean claimPlot(@NonNull Plot plot, @Nullable QueueCoordinator queue) {
816        final BlockBucket claim = classicPlotWorld.CLAIMED_WALL_BLOCK;
817        if (classicPlotWorld.PLACE_TOP_BLOCK && (!claim.isAir() || !claim.equals(classicPlotWorld.WALL_BLOCK))) {
818            return setWall(plot.getId(), claim.toPattern(), null, queue);
819        }
820        return true;
821    }
822
823    @Override
824    public String[] getPlotComponents(@NonNull PlotId plotId) {
825        return ClassicPlotManagerComponent.stringValues();
826    }
827
828    /**
829     * Retrieves the location of where a sign should be for a plot.
830     *
831     * @param plot The plot
832     * @return The location where a sign should be
833     */
834    @Override
835    public Location getSignLoc(@NonNull Plot plot) {
836        plot = plot.getBasePlot(false);
837        final Location bot = plot.getBottomAbs();
838        return Location.at(classicPlotWorld.getWorldName(), bot.getX() - 1, classicPlotWorld.ROAD_HEIGHT + 1, bot.getZ() - 2);
839    }
840
841}