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.location;
020
021import com.google.common.base.Objects;
022import com.google.common.base.Preconditions;
023import com.plotsquared.core.PlotSquared;
024import com.plotsquared.core.plot.Plot;
025import com.plotsquared.core.plot.PlotArea;
026import com.sk89q.worldedit.math.BlockVector2;
027import com.sk89q.worldedit.math.BlockVector3;
028import org.checkerframework.checker.nullness.qual.NonNull;
029import org.checkerframework.checker.nullness.qual.Nullable;
030import org.khelekore.prtree.MBR;
031import org.khelekore.prtree.SimpleMBR;
032
033/**
034 * An unmodifiable 6-tuple (world,x,y,z,yaw,pitch)
035 */
036@SuppressWarnings("unused")
037public sealed class Location extends BlockLoc implements Comparable<Location> permits UncheckedWorldLocation {
038
039    private final float yaw;
040    private final float pitch;
041    private final BlockVector3 blockVector3;
042    private final World<?> world;
043
044    /**
045     * @since 6.9.0
046     */
047    protected Location(
048            final @NonNull World<?> world, final @NonNull BlockVector3 blockVector3,
049            final float yaw, final float pitch
050    ) {
051        super(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ(), yaw, pitch);
052        this.world = Preconditions.checkNotNull(world, "World may not be null");
053        this.blockVector3 = Preconditions.checkNotNull(blockVector3, "Vector may not be null");
054        this.yaw = yaw;
055        this.pitch = pitch;
056    }
057
058    private Location(
059            final @NonNull String worldName, final @NonNull BlockVector3 blockVector3,
060            final float yaw, final float pitch
061    ) {
062        super(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ(), yaw, pitch);
063        Preconditions.checkNotNull(worldName, "World name may not be null");
064        if (worldName.isEmpty()) {
065            this.world = World.nullWorld();
066        } else {
067            this.world = PlotSquared.platform().getPlatformWorld(worldName);
068        }
069        this.blockVector3 = Preconditions.checkNotNull(blockVector3, "Vector may not be null");
070        this.yaw = yaw;
071        this.pitch = pitch;
072    }
073
074    /**
075     * Construct a new location
076     *
077     * @param world        World
078     * @param blockVector3 (x,y,z) vector
079     * @param yaw          yaw
080     * @param pitch        pitch
081     * @return New location
082     */
083    public static @NonNull Location at(
084            final @NonNull String world,
085            final @NonNull BlockVector3 blockVector3, final float yaw, final float pitch
086    ) {
087        return new Location(world, blockVector3, yaw, pitch);
088    }
089
090    /**
091     * Construct a new location with yaw and pitch equal to 0
092     *
093     * @param world        World
094     * @param blockVector3 (x,y,z) vector
095     * @return New location
096     */
097    public static @NonNull Location at(
098            final @NonNull String world,
099            final @NonNull BlockVector3 blockVector3
100    ) {
101        return at(world, blockVector3, 0f, 0f);
102    }
103
104    /**
105     * Construct a new location
106     *
107     * @param world World
108     * @param x     X coordinate
109     * @param y     Y coordinate
110     * @param z     Z coordinate
111     * @param yaw   Yaw
112     * @param pitch Pitch
113     * @return New location
114     */
115    public static @NonNull Location at(
116            final @NonNull String world, final int x, final int y,
117            final int z, final float yaw, final float pitch
118    ) {
119        return at(world, BlockVector3.at(x, y, z), yaw, pitch);
120    }
121
122    /**
123     * Construct a new location with yaw and pitch equal to 0
124     *
125     * @param world World
126     * @param x     X coordinate
127     * @param y     Y coordinate
128     * @param z     Z coordinate
129     * @return New location
130     */
131    public static @NonNull Location at(
132            final @NonNull String world, final int x, final int y,
133            final int z
134    ) {
135        return at(world, BlockVector3.at(x, y, z));
136    }
137
138    /**
139     * Construct a new location
140     *
141     * @param world        World
142     * @param blockVector3 (x,y,z) vector
143     * @param yaw          yaw
144     * @param pitch        pitch
145     * @return New location
146     */
147    public static @NonNull Location at(
148            final @NonNull World<?> world,
149            final @NonNull BlockVector3 blockVector3, final float yaw, final float pitch
150    ) {
151        return new Location(world, blockVector3, yaw, pitch);
152    }
153
154    /**
155     * Construct a new location with yaw and pitch equal to 0
156     *
157     * @param world        World
158     * @param blockVector3 (x,y,z) vector
159     * @return New location
160     */
161    public static @NonNull Location at(
162            final @NonNull World<?> world,
163            final @NonNull BlockVector3 blockVector3
164    ) {
165        return at(world, blockVector3, 0f, 0f);
166    }
167
168    /**
169     * Construct a new location
170     *
171     * @param world World
172     * @param x     X coordinate
173     * @param y     Y coordinate
174     * @param z     Z coordinate
175     * @param yaw   Yaw
176     * @param pitch Pitch
177     * @return New location
178     */
179    public static @NonNull Location at(
180            final @NonNull World<?> world, final int x, final int y,
181            final int z, final float yaw, final float pitch
182    ) {
183        return at(world, BlockVector3.at(x, y, z), yaw, pitch);
184    }
185
186    /**
187     * Construct a new location with yaw and pitch equal to 0
188     *
189     * @param world World
190     * @param x     X coordinate
191     * @param y     Y coordinate
192     * @param z     Z coordinate
193     * @return New location
194     */
195    public static @NonNull Location at(
196            final @NonNull World<?> world, final int x, final int y,
197            final int z
198    ) {
199        return at(world, BlockVector3.at(x, y, z));
200    }
201
202    /**
203     * Get the world object
204     *
205     * @return World object
206     */
207    public @NonNull World<?> getWorld() {
208        return this.world;
209    }
210
211    /**
212     * Get the name of the world this location is in
213     *
214     * @return World name
215     */
216    public @NonNull String getWorldName() {
217        return this.world.getName();
218    }
219
220    /**
221     * Get the X coordinate
222     *
223     * @return X coordinate
224     */
225    public int getX() {
226        return this.blockVector3.getBlockX();
227    }
228
229    /**
230     * Get the Y coordinate
231     *
232     * @return Y coordinate
233     */
234    public int getY() {
235        return this.blockVector3.getY();
236    }
237
238    /**
239     * Get the Z coordinate
240     *
241     * @return Z coordinate
242     */
243    public int getZ() {
244        return this.blockVector3.getZ();
245    }
246
247    /**
248     * Get the {@link PlotArea}, if any, that contains this location
249     *
250     * @return Plot area containing the location, or {@code null}
251     */
252    public @Nullable PlotArea getPlotArea() {
253        return PlotSquared.get().getPlotAreaManager().getPlotArea(this);
254    }
255
256    /**
257     * Get the owned {@link Plot}, if any, that contains this location
258     *
259     * @return Plot containing the location, or {@code null}
260     */
261    public @Nullable Plot getOwnedPlot() {
262        final PlotArea area = this.getPlotArea();
263        if (area != null) {
264            return area.getOwnedPlot(this);
265        } else {
266            return null;
267        }
268    }
269
270    /**
271     * Get the (absolute) owned {@link Plot}, if any, that contains this location
272     *
273     * @return (Absolute) plot containing the location, or {@code null}
274     */
275    public @Nullable Plot getOwnedPlotAbs() {
276        final PlotArea area = this.getPlotArea();
277        if (area != null) {
278            return area.getOwnedPlotAbs(this);
279        } else {
280            return null;
281        }
282    }
283
284    /**
285     * Check whether the location belongs to a plot area
286     *
287     * @return {@code true} if the location belongs to a plot area, else {@code false}
288     */
289    public boolean isPlotArea() {
290        return this.getPlotArea() != null;
291    }
292
293    /**
294     * Check whether the location belongs to a plot road
295     *
296     * @return {@code true} if the location belongs to a plot road, else {@code false}
297     */
298    public boolean isPlotRoad() {
299        final PlotArea area = this.getPlotArea();
300        return area != null && area.getPlotAbs(this) == null;
301    }
302
303    /**
304     * Checks if anyone owns a plot at the current location.
305     *
306     * @return {@code true} if the location is a road, not a plot area, or if the plot is unclaimed.
307     */
308    public boolean isUnownedPlotArea() {
309        final PlotArea area = this.getPlotArea();
310        return area != null && area.getOwnedPlotAbs(this) == null;
311    }
312
313    /**
314     * Get the absolute {@link Plot}, if any, that contains this location
315     *
316     * @return (Absolute) plot containing the location, or {@code null}
317     */
318    public @Nullable Plot getPlotAbs() {
319        final PlotArea area = this.getPlotArea();
320        if (area != null) {
321            return area.getPlotAbs(this);
322        } else {
323            return null;
324        }
325    }
326
327    /**
328     * Get the {@link Plot}, if any, that contains this location
329     *
330     * @return plot containing the location, or {@code null}
331     */
332    public @Nullable Plot getPlot() {
333        final PlotArea area = this.getPlotArea();
334        if (area != null) {
335            return area.getPlot(this);
336        } else {
337            return null;
338        }
339    }
340
341    /**
342     * Get the coordinates of the chunk that contains this location
343     *
344     * @return Chunk coordinates
345     */
346    public @NonNull BlockVector2 getChunkLocation() {
347        return BlockVector2.at(this.getX() >> 4, this.getZ() >> 4);
348    }
349
350    /**
351     * Return a new location offset by the given coordinates
352     *
353     * @param x X offset
354     * @param y Y offset
355     * @param z Z offset
356     * @return New location
357     */
358    public @NonNull Location add(final int x, final int y, final int z) {
359        return new Location(this.world, this.blockVector3.add(x, y, z), this.yaw, this.pitch);
360    }
361
362    /**
363     * Return a new location using the given X coordinate
364     *
365     * @param x New X coordinate
366     * @return New location
367     */
368    public @NonNull Location withX(final int x) {
369        return new Location(this.world, this.blockVector3.withX(x), this.yaw, this.pitch);
370    }
371
372    /**
373     * Return a new location using the given Y coordinate
374     *
375     * @param y New Y coordinate
376     * @return New location
377     */
378    public @NonNull Location withY(final int y) {
379        return new Location(this.world, this.blockVector3.withY(y), this.yaw, this.pitch);
380    }
381
382    /**
383     * Return a new location using the given Z coordinate
384     *
385     * @param z New Z coordinate
386     * @return New location
387     */
388    public @NonNull Location withZ(final int z) {
389        return new Location(this.world, this.blockVector3.withZ(z), this.yaw, this.pitch);
390    }
391
392    /**
393     * Return a new location using the given yaw
394     *
395     * @param yaw New yaw
396     * @return New location
397     */
398    public @NonNull Location withYaw(final float yaw) {
399        return new Location(this.world, this.blockVector3, yaw, this.pitch);
400    }
401
402    /**
403     * Return a new location using the given pitch
404     *
405     * @param pitch New pitch
406     * @return New location
407     */
408    public @NonNull Location withPitch(final float pitch) {
409        return new Location(this.world, this.blockVector3, this.yaw, pitch);
410    }
411
412    /**
413     * Return a new location using the given world
414     *
415     * @param world New world
416     * @return New location
417     */
418    public @NonNull Location withWorld(final @NonNull String world) {
419        return new Location(world, this.blockVector3, this.yaw, this.pitch);
420    }
421
422    public double getEuclideanDistanceSquared(final @NonNull Location l2) {
423        double x = getX() - l2.getX();
424        double y = getY() - l2.getY();
425        double z = getZ() - l2.getZ();
426        return x * x + y * y + z * z;
427    }
428
429    public double getEuclideanDistance(final @NonNull Location l2) {
430        return Math.sqrt(getEuclideanDistanceSquared(l2));
431    }
432
433    /**
434     * Return a new location offset by (-) the given coordinates
435     *
436     * @param x X offset
437     * @param y Y offset
438     * @param z Z offset
439     * @return New location
440     */
441    public @NonNull Location subtract(int x, int y, int z) {
442        return this.add(-x, -y, -z);
443    }
444
445    /**
446     * Get a minimum bounding rectangle that contains this location only
447     *
448     * @return Minimum bounding rectangle
449     */
450    public @NonNull MBR toMBR() {
451        return new SimpleMBR(this.getX(), this.getX(), this.getY(), this.getY(), this.getZ(),
452                this.getZ()
453        );
454    }
455
456    @Override
457    public int compareTo(final @NonNull Location o) {
458        if (this.getX() == o.getX() && this.getY() == o.getY() || this.getZ() == o.getZ()) {
459            return 0;
460        }
461        if (this.getX() < o.getX() && this.getY() < o.getY() && this.getZ() < o.getZ()) {
462            return -1;
463        }
464        return 1;
465    }
466
467    @Override
468    public boolean equals(final Object o) {
469        if (this == o) {
470            return true;
471        }
472        if (o == null || getClass() != o.getClass()) {
473            return false;
474        }
475        if (!super.equals(o)) {
476            return false;
477        }
478        final Location location = (Location) o;
479        return Float.compare(location.getYaw(), getYaw()) == 0
480                && Float.compare(location.getPitch(), getPitch()) == 0 && Objects
481                .equal(getBlockVector3(), location.getBlockVector3()) && Objects
482                .equal(getWorld(), location.getWorld());
483    }
484
485    @Override
486    public int hashCode() {
487        return Objects
488                .hashCode(super.hashCode(), getYaw(), getPitch(), getBlockVector3(), getWorld());
489    }
490
491    @Override
492    public String toString() {
493        return "\"plotsquaredlocation\":{\"x\":" + this.getX() + ",\"y\":" + this.getY() + ",\"z\":"
494                + this.getZ() + ",\"yaw\":" + this.yaw + ",\"pitch\":" + this.pitch + ",\"world\":\""
495                + this.world + "\"}";
496    }
497
498    public float getYaw() {
499        return this.yaw;
500    }
501
502    public float getPitch() {
503        return this.pitch;
504    }
505
506    public BlockVector3 getBlockVector3() {
507        return this.blockVector3;
508    }
509
510}