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.plotsquared.core.location.Direction;
022import com.plotsquared.core.location.Location;
023import com.plotsquared.core.player.PlotPlayer;
024import com.plotsquared.core.plot.Plot;
025import com.plotsquared.core.plot.PlotArea;
026import com.plotsquared.core.plot.PlotId;
027import com.plotsquared.core.queue.QueueCoordinator;
028import com.plotsquared.core.util.HashUtil;
029import com.plotsquared.core.util.RegionManager;
030import com.sk89q.worldedit.regions.CuboidRegion;
031import org.apache.logging.log4j.LogManager;
032import org.apache.logging.log4j.Logger;
033import org.checkerframework.checker.nullness.qual.NonNull;
034import org.checkerframework.checker.nullness.qual.Nullable;
035
036import java.util.Iterator;
037import java.util.Set;
038
039/**
040 * A plot manager with a square grid layout, with square shaped plots.
041 */
042public abstract class SquarePlotManager extends GridPlotManager {
043
044    private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + SquarePlotManager.class.getSimpleName());
045
046    private final SquarePlotWorld squarePlotWorld;
047    private final RegionManager regionManager;
048
049    public SquarePlotManager(final @NonNull SquarePlotWorld squarePlotWorld, final @NonNull RegionManager regionManager) {
050        super(squarePlotWorld);
051        this.squarePlotWorld = squarePlotWorld;
052        this.regionManager = regionManager;
053    }
054
055    @Override
056    public boolean clearPlot(
057            final @NonNull Plot plot,
058            final @Nullable Runnable whenDone,
059            @Nullable PlotPlayer<?> actor,
060            @Nullable QueueCoordinator queue
061    ) {
062        final Set<CuboidRegion> regions = plot.getRegions();
063        Runnable run = new Runnable() {
064            @Override
065            public void run() {
066                if (regions.isEmpty()) {
067                    if (whenDone != null) {
068                        whenDone.run();
069                    }
070                    return;
071                }
072                Iterator<CuboidRegion> iterator = regions.iterator();
073                CuboidRegion region = iterator.next();
074                iterator.remove();
075                final Location pos1 = Location.at(plot.getWorldName(), region.getMinimumPoint());
076                final Location pos2 = Location.at(plot.getWorldName(), region.getMaximumPoint());
077                regionManager.regenerateRegion(pos1, pos2, false, this);
078            }
079        };
080        run.run();
081        return true;
082    }
083
084    @Override
085    public Location getPlotTopLocAbs(@NonNull PlotId plotId) {
086        int px = plotId.getX();
087        int pz = plotId.getY();
088        int x = (squarePlotWorld.ROAD_OFFSET_X + (px * (squarePlotWorld.ROAD_WIDTH + squarePlotWorld.PLOT_WIDTH))) - (int) Math
089                .floor(squarePlotWorld.ROAD_WIDTH / 2) - 1;
090        int z = (squarePlotWorld.ROAD_OFFSET_Z + (pz * (squarePlotWorld.ROAD_WIDTH + squarePlotWorld.PLOT_WIDTH))) - (int) Math
091                .floor(squarePlotWorld.ROAD_WIDTH / 2) - 1;
092        return Location.at(squarePlotWorld.getWorldName(), x, squarePlotWorld.getMaxGenHeight(), z);
093    }
094
095    @Override
096    public PlotId getPlotIdAbs(int x, int y, int z) {
097        if (squarePlotWorld.ROAD_OFFSET_X != 0) {
098            x -= squarePlotWorld.ROAD_OFFSET_X;
099        }
100        if (squarePlotWorld.ROAD_OFFSET_Z != 0) {
101            z -= squarePlotWorld.ROAD_OFFSET_Z;
102        }
103        int pathWidthLower;
104        int end;
105        if (squarePlotWorld.ROAD_WIDTH == 0) {
106            pathWidthLower = -1;
107            end = squarePlotWorld.PLOT_WIDTH;
108        } else {
109            if ((squarePlotWorld.ROAD_WIDTH % 2) == 0) {
110                pathWidthLower = (squarePlotWorld.ROAD_WIDTH / 2) - 1;
111            } else {
112                pathWidthLower = squarePlotWorld.ROAD_WIDTH / 2;
113            }
114            end = pathWidthLower + squarePlotWorld.PLOT_WIDTH;
115        }
116        int size = squarePlotWorld.PLOT_WIDTH + squarePlotWorld.ROAD_WIDTH;
117        int dx = Math.floorDiv(x, size) + 1;
118        int rx = Math.floorMod(x, size);
119        int dz = Math.floorDiv(z, size) + 1;
120        int rz = Math.floorMod(z, size);
121        if (rz <= pathWidthLower || rz > end || rx <= pathWidthLower || rx > end) {
122            return null;
123        } else {
124            return PlotId.of(dx, dz);
125        }
126    }
127
128    public PlotId getNearestPlotId(@NonNull PlotArea plotArea, int x, int y, int z) {
129        SquarePlotWorld dpw = (SquarePlotWorld) plotArea;
130        if (dpw.ROAD_OFFSET_X != 0) {
131            x -= dpw.ROAD_OFFSET_X;
132        }
133        if (dpw.ROAD_OFFSET_Z != 0) {
134            z -= dpw.ROAD_OFFSET_Z;
135        }
136        int size = dpw.PLOT_WIDTH + dpw.ROAD_WIDTH;
137        int idx;
138        if (x < 0) {
139            idx = x / size;
140        } else {
141            idx = (x / size) + 1;
142        }
143        int idz;
144        if (z < 0) {
145            idz = z / size;
146        } else {
147            idz = (z / size) + 1;
148        }
149        return PlotId.of(idx, idz);
150    }
151
152    @Override
153    public PlotId getPlotId(int x, int y, int z) {
154        try {
155            x -= squarePlotWorld.ROAD_OFFSET_X;
156            z -= squarePlotWorld.ROAD_OFFSET_Z;
157            int size = squarePlotWorld.PLOT_WIDTH + squarePlotWorld.ROAD_WIDTH;
158            int pathWidthLower;
159            int end;
160            if (squarePlotWorld.ROAD_WIDTH == 0) {
161                pathWidthLower = -1;
162                end = squarePlotWorld.PLOT_WIDTH;
163            } else {
164                if ((squarePlotWorld.ROAD_WIDTH % 2) == 0) {
165                    pathWidthLower = (squarePlotWorld.ROAD_WIDTH / 2) - 1;
166                } else {
167                    pathWidthLower = squarePlotWorld.ROAD_WIDTH / 2;
168                }
169                end = pathWidthLower + squarePlotWorld.PLOT_WIDTH;
170            }
171            int dx = Math.floorDiv(x, size) + 1;
172            int rx = Math.floorMod(x, size);
173            int dz = Math.floorDiv(z, size) + 1;
174            int rz = Math.floorMod(z, size);
175            PlotId id = PlotId.of(dx, dz);
176            boolean[] merged = new boolean[]{rz <= pathWidthLower, rx > end, rz > end, rx <= pathWidthLower};
177            int hash = HashUtil.hash(merged);
178            // Not merged, and no need to check if it is
179            if (hash == 0) {
180                return id;
181            }
182            Plot plot = squarePlotWorld.getOwnedPlotAbs(id);
183            // Not merged, and standing on road
184            if (plot == null) {
185                return null;
186            }
187            switch (hash) {
188                case 8:
189                    // north
190                    return plot.isMerged(Direction.NORTH) ? id : null;
191                case 4:
192                    // east
193                    return plot.isMerged(Direction.EAST) ? id : null;
194                case 2:
195                    // south
196                    return plot.isMerged(Direction.SOUTH) ? id : null;
197                case 1:
198                    // west
199                    return plot.isMerged(Direction.WEST) ? id : null;
200                case 12:
201                    // northeast
202                    return plot.isMerged(Direction.NORTHEAST) ? id : null;
203                case 6:
204                    // southeast
205                    return plot.isMerged(Direction.SOUTHEAST) ? id : null;
206                case 3:
207                    // southwest
208                    return plot.isMerged(Direction.SOUTHWEST) ? id : null;
209                case 9:
210                    // northwest
211                    return plot.isMerged(Direction.NORTHWEST) ? id : null;
212            }
213        } catch (Exception ignored) {
214            LOGGER.error("Invalid plot / road width in settings.yml for world: {}", squarePlotWorld.getWorldName());
215        }
216        return null;
217    }
218
219    /**
220     * Get the bottom plot loc (some basic math).
221     */
222    @Override
223    public Location getPlotBottomLocAbs(@NonNull PlotId plotId) {
224        int px = plotId.getX();
225        int pz = plotId.getY();
226        int x = (squarePlotWorld.ROAD_OFFSET_X + (px * (squarePlotWorld.ROAD_WIDTH + squarePlotWorld.PLOT_WIDTH))) - squarePlotWorld.PLOT_WIDTH
227                - (int) Math.floor(squarePlotWorld.ROAD_WIDTH / 2);
228        int z = (squarePlotWorld.ROAD_OFFSET_Z + (pz * (squarePlotWorld.ROAD_WIDTH + squarePlotWorld.PLOT_WIDTH))) - squarePlotWorld.PLOT_WIDTH
229                - (int) Math.floor(squarePlotWorld.ROAD_WIDTH / 2);
230        return Location.at(squarePlotWorld.getWorldName(), x, squarePlotWorld.getMinGenHeight(), z);
231    }
232
233}