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.bukkit.entity;
020
021import com.plotsquared.bukkit.BukkitPlatform;
022import org.bukkit.Chunk;
023import org.bukkit.Location;
024import org.bukkit.World;
025import org.bukkit.entity.Entity;
026import org.bukkit.metadata.FixedMetadataValue;
027
028public class TeleportEntityWrapper extends EntityWrapper {
029
030    private Location oldLocation;
031    private boolean gravityOld;
032    private boolean invulnerableOld;
033    private int fireTicksOld;
034    private int livingTicksOld;
035
036    public TeleportEntityWrapper(final Entity entity) {
037        super(entity);
038    }
039
040    @Override
041    public Entity spawn(final World world, final int xOffset, final int zOffset) {
042        if (!getEntity().getLocation().getChunk().equals(oldLocation.getChunk())) {
043            final Location oldLocation = this.oldLocation.clone();
044            oldLocation.add(xOffset, 0, xOffset);
045            getEntity().teleport(oldLocation);
046            getEntity().setGravity(gravityOld);
047            getEntity().setInvulnerable(invulnerableOld);
048            getEntity().setFireTicks(fireTicksOld);
049            getEntity().setTicksLived(livingTicksOld);
050            getEntity().removeMetadata("ps-tmp-teleport", BukkitPlatform.getPlugin(BukkitPlatform.class));
051        }
052        return getEntity();
053    }
054
055    @Override
056    public void saveEntity() {
057        if (getEntity().hasMetadata("ps-tmp-teleport")) {
058            this.oldLocation = (Location) this.getEntity().getMetadata("ps-tmp-teleport").get(0);
059        } else {
060            this.oldLocation = this.getEntity().getLocation();
061        }
062
063        // To account for offsets in the chunk manager
064        this.oldLocation = oldLocation.clone();
065        this.oldLocation.setX(this.getX());
066        this.oldLocation.setY(this.getY());
067        this.oldLocation.setZ(this.getZ());
068
069        this.gravityOld = this.getEntity().hasGravity();
070        this.getEntity().setGravity(false);
071        this.invulnerableOld = this.getEntity().isInvulnerable();
072        this.getEntity().setInvulnerable(true);
073        this.fireTicksOld = this.getEntity().getFireTicks();
074        this.livingTicksOld = this.getEntity().getTicksLived();
075        this.getEntity().setMetadata(
076                "ps-tmp-teleport",
077                new FixedMetadataValue(BukkitPlatform.getPlugin(BukkitPlatform.class), oldLocation)
078        );
079        final Chunk newChunk = getNewChunk();
080        this.getEntity().teleport(
081                new Location(newChunk.getWorld(), newChunk.getX() << 4, 5000, newChunk.getZ() << 4));
082    }
083
084    private Chunk getNewChunk() {
085        final Chunk oldChunk = oldLocation.getChunk();
086        Chunk chunk = null;
087
088        for (Chunk lChunk : oldChunk.getWorld().getLoadedChunks()) {
089            if (!lChunk.equals(oldChunk) && lChunk.isLoaded()) {
090                chunk = lChunk;
091                break;
092            }
093        }
094        if (chunk == null) {
095            for (int dx = 1; dx < Integer.MAX_VALUE; dx++) {
096                for (int dz = 0; dz < Integer.MAX_VALUE; dz++) {
097                    if ((chunk = getChunkRelative(oldChunk, dx, dz)).isLoaded()) {
098                        break;
099                    } else if ((chunk = getChunkRelative(oldChunk, -dx, dz)).isLoaded()) {
100                        break;
101                    } else if ((chunk = getChunkRelative(oldChunk, dx, -dz)).isLoaded()) {
102                        break;
103                    } else if ((chunk = getChunkRelative(oldChunk, -dx, -dz)).isLoaded()) {
104                        break;
105                    }
106                }
107            }
108        }
109        return chunk;
110    }
111
112    private Chunk getChunkRelative(final Chunk chunk, final int dx, final int dz) {
113        return chunk.getWorld().getChunkAt(chunk.getX() + dx, chunk.getZ() + dz);
114    }
115
116}