package in.twizmwaz.cardinal.module.modules.damageIndicator;
import com.google.common.base.Optional;
import in.twizmwaz.cardinal.Cardinal;
import in.twizmwaz.cardinal.event.CardinalDeathEvent;
import in.twizmwaz.cardinal.module.modules.observers.ObserverModule;
import in.twizmwaz.cardinal.module.modules.team.TeamModule;
import in.twizmwaz.cardinal.util.Numbers;
import in.twizmwaz.cardinal.util.PacketUtils;
import in.twizmwaz.cardinal.util.Teams;
import in.twizmwaz.cardinal.util.Watchers;
import net.minecraft.server.Packet;
import net.minecraft.server.PacketPlayOutEntity;
import net.minecraft.server.PacketPlayOutEntityDestroy;
import net.minecraft.server.PacketPlayOutEntityTeleport;
import net.minecraft.server.PacketPlayOutSpawnEntityLiving;
import org.apache.commons.lang.ArrayUtils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.player.PlayerUseUnknownEntityEvent;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
class PlayerBoundingBox implements Listener {
private static double MAX_DISTANCE = 10;
private UUID player;
private Optional<TeamModule> team;
private List<UUID> viewers = new ArrayList<>();
private List<Integer> zombieID = new ArrayList<>();
private int mainTask;
protected PlayerBoundingBox(UUID player, Optional<TeamModule> team) {
this.player = player;
this.team = team;
for (int i = 0; i < 4; i++) {
zombieID.add(Bukkit.allocateEntityId());
}
Bukkit.getPluginManager().registerEvents(this, Cardinal.getInstance());
mainTask = Bukkit.getScheduler().scheduleSyncDelayedTask(Cardinal.getInstance(), new Runnable() {
@Override
public void run() {
updateAndTeleport(null);
}
}, 20L);
}
public void destroy(){
Bukkit.getScheduler().cancelTask(mainTask);
broadcastRemovePacket();
HandlerList.unregisterAll(this);
}
@EventHandler
public void onZombieAttack(PlayerUseUnknownEntityEvent event) {
if (shouldShow(event.getPlayer()) && event.isAttack() && this.zombieID.contains(event.getEntityId())) {
((CraftPlayer)event.getPlayer()).getHandle().attack(((CraftPlayer)Bukkit.getPlayer(player)).getHandle());
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerMove(PlayerTeleportEvent event) {
if (event.getPlayer().getUniqueId().equals(this.player)) {
updateAndTeleport(event.getTo().position());
} else {
sendOrRemoveZombies(event.getPlayer(), event.getTo().position(), Bukkit.getPlayer(player).getLocation().position());
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerMove(PlayerMoveEvent event) {
if (event.getPlayer().getUniqueId().equals(this.player)) {
for (Player player : Bukkit.getOnlinePlayers()) {
sendOrRemoveZombies(player, player.getLocation().position(), event.getTo().position());
}
Vector diff = Numbers.clone(event.getTo().position()).subtract(event.getFrom().position());
relativeMoveBoundingBox(Math.round(4096 * (diff.getX())),
Math.round(4096 * (diff.getY())), Math.round(4096 * (diff.getZ())), event.getPlayer().isOnGround());
} else {
sendOrRemoveZombies(event.getPlayer(), event.getTo().position(), Bukkit.getPlayer(player).getLocation().position());
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerDeath(CardinalDeathEvent event) {
if (event.getPlayer().getUniqueId().equals(this.player)) {
broadcastRemovePacket();
} else {
sendRemovePacket(event.getPlayer());
}
}
private boolean shouldShow(Player player) {
Optional<TeamModule> team = Teams.getTeamByPlayer(player);
return !ObserverModule.testObserverOrDead(player) && (team.isPresent() && !team.get().equals(this.team.get()));
}
public void sendOrRemoveZombies(Player player, Vector playerPos, Vector bbPos) {
if (shouldShow(player) && bbPos.distance(playerPos) < MAX_DISTANCE) {
sendSpawnPackets(player);
} else {
sendRemovePacket(player);
}
}
public void updateAndTeleport(Vector loc) {
if (loc == null) loc = Bukkit.getPlayer(this.player).getLocation().position();
for (Player player : Bukkit.getOnlinePlayers()) {
sendOrRemoveZombies(player, player.getLocation().position(), loc);
}
teleportBoundingBox(loc, Bukkit.getPlayer(this.player).isOnGround());
}
public void teleportBoundingBox(Vector to, boolean onGround) {
int i = 0;
for (int x = -1; x < 2; x += 2) {
for (int z = -1; z < 2; z += 2) {
Packet teleportPacket = new PacketPlayOutEntityTeleport(zombieID.get(i),
to.getX() + (x * DamageIndicator.OFFSET), to.getY() , to.getZ() + (z * DamageIndicator.OFFSET),
(byte) 2, (byte) 0, onGround);
PacketUtils.broadcastPacketByUUID(teleportPacket, viewers);
i++;
}
}
}
public void relativeMoveBoundingBox(long x, long y, long z, boolean onGround) {
for (Integer zombie : zombieID) {
PacketUtils.broadcastPacketByUUID(new PacketPlayOutEntity.PacketPlayOutRelEntityMove(zombie, x, y, z, onGround), viewers);
}
}
public void sendSpawnPackets(Player viewer) {
if (viewers.contains(viewer.getUniqueId())) return;
Player player = Bukkit.getPlayer(this.player);
Location loc = player.getLocation();
int i = 0;
for (int x = -1; x < 2; x += 2) {
for (int z = -1; z < 2; z += 2) {
Packet spawnPacket = new PacketPlayOutSpawnEntityLiving(
zombieID.get(i++), UUID.randomUUID(), 54, // Entity id, UUID, and type (Zombie)
loc.getX() + (x * DamageIndicator.OFFSET), loc.getY(),// X, Y
loc.getZ() + (z * DamageIndicator.OFFSET), // and Z coords
0, 0, 0, // X, Y and Z Motion
(byte) 2, (byte) 0, (byte) 2, // Yaw, Pitch and Head Pitch
Watchers.toList(Watchers.INVISIBLE)); // Metadata
PacketUtils.sendPacket(viewer, spawnPacket);
}
}
viewers.add(viewer.getUniqueId());
}
private Packet getRemovePacket() {
int[] ids = ArrayUtils.toPrimitive(zombieID.toArray(new Integer[4]));
return new PacketPlayOutEntityDestroy(ids);
}
public void sendRemovePacket(Player player) {
if (!viewers.contains(player.getUniqueId())) return;
PacketUtils.sendPacket(player, getRemovePacket());
viewers.remove(player.getUniqueId());
}
private void broadcastRemovePacket() {
PacketUtils.broadcastPacketByUUID(getRemovePacket(), viewers);
}
}