NPCLib/api/src/main/java/net/jitse/npclib/internal/NPCBase.java

369 lines
12 KiB
Java
Raw Normal View History

2018-04-26 13:52:49 +02:00
/*
* Copyright (c) 2018 Jitse Boonstra
*/
2019-08-03 13:47:12 +02:00
package net.jitse.npclib.internal;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import net.jitse.npclib.NPCLib;
import net.jitse.npclib.api.NPC;
import net.jitse.npclib.api.events.NPCHideEvent;
import net.jitse.npclib.api.events.NPCShowEvent;
import net.jitse.npclib.api.skin.Skin;
2020-07-14 15:13:26 +02:00
import net.jitse.npclib.api.state.NPCAnimation;
2019-10-20 10:48:12 +02:00
import net.jitse.npclib.api.state.NPCSlot;
import net.jitse.npclib.api.state.NPCState;
import net.jitse.npclib.hologram.Hologram;
2020-04-18 10:16:42 +02:00
import net.jitse.npclib.utilities.MathUtil;
2018-04-26 13:52:49 +02:00
import org.bukkit.Bukkit;
import org.bukkit.Location;
2019-09-22 21:09:48 +02:00
import org.bukkit.World;
2018-04-26 13:52:49 +02:00
import org.bukkit.entity.Player;
2019-10-20 10:48:12 +02:00
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
2018-04-26 13:52:49 +02:00
import java.util.*;
public abstract class NPCBase implements NPC, NPCPacketHandler {
2018-04-26 13:52:49 +02:00
protected final int entityId = Integer.MAX_VALUE - NPCManager.getAllNPCs().size();
protected final Set<UUID> hasTeamRegistered = new HashSet<>();
protected final Set<NPCState> activeStates = EnumSet.noneOf(NPCState.class);
2018-04-26 13:52:49 +02:00
private final Set<UUID> shown = new HashSet<>();
private final Set<UUID> autoHidden = new HashSet<>();
2019-08-03 13:47:12 +02:00
protected double cosFOV = Math.cos(Math.toRadians(60));
2020-04-12 17:01:18 +02:00
// 12/4/20, JMB: Changed the UUID in order to enable LabyMod Emotes:
// This gives a format similar to: 528086a2-4f5f-2ec2-0000-000000000000
protected UUID uuid = new UUID(new Random().nextLong(), 0);
protected String name = uuid.toString().replace("-", "").substring(0, 10);
protected GameProfile gameProfile = new GameProfile(uuid, name);
2018-04-26 13:52:49 +02:00
2019-08-03 13:47:12 +02:00
protected NPCLib instance;
protected List<String> text;
2018-04-26 13:52:49 +02:00
protected Location location;
2019-08-03 13:47:12 +02:00
protected Skin skin;
protected Hologram hologram;
2018-04-26 13:52:49 +02:00
protected final Map<NPCSlot, ItemStack> items = new EnumMap<>(NPCSlot.class);
2019-10-20 10:48:12 +02:00
// Storage for per-player text;
protected final Map<UUID, List<String>> uniqueText = new HashMap<>();
protected final Map<UUID, Hologram> textDisplayHolograms = new HashMap<>();
public NPCBase(NPCLib instance, List<String> text) {
2019-08-03 13:47:12 +02:00
this.instance = instance;
this.text = text == null ? Collections.emptyList() : text;
2018-04-26 13:52:49 +02:00
NPCManager.add(this);
}
2019-08-03 13:47:12 +02:00
public NPCLib getInstance() {
return instance;
}
2020-04-26 12:09:58 +02:00
@Override
public Hologram getPlayerHologram(Player player){
Hologram playerHologram = textDisplayHolograms.getOrDefault(player.getUniqueId(), null);
return playerHologram;
}
@Override
public NPC setPlayerLines(List<String> uniqueLines, Player targetPlayer) {
uniqueText.put(targetPlayer.getUniqueId(), uniqueLines);
return this;
}
@Override
public NPC setPlayerLines(List<String> uniqueLines, Player targetPlayer, boolean update) {
List<String> originalLines = getPlayerLines(targetPlayer);
setPlayerLines(uniqueLines, targetPlayer);
if (update){
if (originalLines.size() != uniqueLines.size()){ // recreate the entire hologram
Hologram originalhologram = getPlayerHologram(targetPlayer);
originalhologram.hide(targetPlayer); // essentially destroy the hologram
textDisplayHolograms.remove(targetPlayer.getUniqueId()); // remove the old obj
}
Hologram hologram = getPlayerHologram(targetPlayer); //
List<Object> updatePackets = hologram.getUpdatePackets(getPlayerLines(targetPlayer));
hologram.update(targetPlayer, updatePackets);
hologram.show(targetPlayer);
}
return this;
}
@Override
public List<String> getPlayerLines(Player targetPlayer) {
return uniqueText.getOrDefault(targetPlayer.getUniqueId(), text);
}
@Override
public UUID getUniqueId() {
2020-04-22 05:12:19 +02:00
return uuid;
}
2019-08-03 13:47:12 +02:00
@Override
public String getId() {
return name;
}
@Override
public NPC setSkin(Skin skin) {
this.skin = skin;
2019-08-07 11:16:34 +02:00
gameProfile.getProperties().get("textures").clear();
if (skin != null)
2019-08-07 11:16:34 +02:00
gameProfile.getProperties().put("textures", new Property("textures", skin.getValue(), skin.getSignature()));
2019-08-03 13:47:12 +02:00
return this;
}
@Override
2018-04-26 13:52:49 +02:00
public void destroy() {
NPCManager.remove(this);
// Destroy NPC for every player that is still seeing it.
for (UUID uuid : shown) {
if (autoHidden.contains(uuid)) {
continue;
}
hide(Bukkit.getPlayer(uuid), true);
2018-04-26 13:52:49 +02:00
}
}
public void disableFOV() {
2020-04-18 10:16:42 +02:00
this.cosFOV = 0;
}
public void setFOV(double fov) {
2019-08-03 13:47:12 +02:00
this.cosFOV = Math.cos(Math.toRadians(fov));
}
2018-04-26 13:52:49 +02:00
public Set<UUID> getShown() {
return shown;
}
public Set<UUID> getAutoHidden() {
return autoHidden;
}
2019-08-03 13:47:12 +02:00
@Override
2018-04-26 13:52:49 +02:00
public Location getLocation() {
return location;
}
2019-09-22 21:09:48 +02:00
@Override
public World getWorld() {
return location != null ? location.getWorld() : null;
}
2018-04-26 13:52:49 +02:00
public int getEntityId() {
return entityId;
}
2019-08-03 13:47:12 +02:00
@Override
public boolean isShown(Player player) {
2018-04-26 13:52:49 +02:00
return shown.contains(player.getUniqueId()) && !autoHidden.contains(player.getUniqueId());
}
2019-08-03 13:47:12 +02:00
@Override
public NPC setLocation(Location location) {
this.location = location;
2019-08-03 13:47:12 +02:00
return this;
}
2019-08-03 13:47:12 +02:00
@Override
public NPC create() {
createPackets();
2019-08-03 13:47:12 +02:00
return this;
}
2018-04-26 13:52:49 +02:00
public void onLogout(Player player) {
getAutoHidden().remove(player.getUniqueId());
getShown().remove(player.getUniqueId()); // Don't need to use NPC#hide since the entity is not registered in the NMS server.
hasTeamRegistered.remove(player.getUniqueId());
}
2020-04-18 10:16:42 +02:00
public boolean inRangeOf(Player player) {
if (!player.getWorld().equals(location.getWorld())) {
// No need to continue our checks, they are in different worlds.
return false;
}
// If Bukkit doesn't track the NPC entity anymore, bypass the hiding distance variable.
// This will cause issues otherwise (e.g. custom skin disappearing).
double hideDistance = instance.getAutoHideDistance();
double distanceSquared = player.getLocation().distanceSquared(location);
double bukkitRange = Bukkit.getViewDistance() << 4;
return distanceSquared <= MathUtil.square(hideDistance) && distanceSquared <= MathUtil.square(bukkitRange);
}
public boolean inViewOf(Player player) {
Vector dir = location.toVector().subtract(player.getEyeLocation().toVector()).normalize();
return dir.dot(player.getLocation().getDirection()) >= cosFOV;
}
2019-08-03 13:47:12 +02:00
@Override
2018-04-26 13:52:49 +02:00
public void show(Player player) {
show(player, false);
}
public void show(Player player, boolean auto) {
2019-08-03 13:47:12 +02:00
NPCShowEvent event = new NPCShowEvent(this, player, auto);
Bukkit.getServer().getPluginManager().callEvent(event);
2018-04-26 13:52:49 +02:00
if (event.isCancelled()) {
return;
}
2020-04-18 10:16:42 +02:00
if (isShown(player)) {
throw new IllegalArgumentException("NPC is already shown to player");
}
2018-04-26 13:52:49 +02:00
if (auto) {
sendShowPackets(player);
2019-10-20 10:48:12 +02:00
sendMetadataPacket(player);
sendEquipmentPackets(player);
2020-04-18 10:16:42 +02:00
// NPC is auto-shown now, we can remove the UUID from the set.
autoHidden.remove(player.getUniqueId());
} else {
// Adding the UUID to the set.
2018-04-26 13:52:49 +02:00
shown.add(player.getUniqueId());
2020-04-18 10:16:42 +02:00
if (inRangeOf(player) && inViewOf(player)) {
// The player can see the NPC and is in range, send the packets.
2018-04-26 13:52:49 +02:00
sendShowPackets(player);
2019-10-20 10:48:12 +02:00
sendMetadataPacket(player);
sendEquipmentPackets(player);
2018-04-26 13:52:49 +02:00
} else {
2020-04-18 10:16:42 +02:00
// We'll wait until we can show the NPC to the player via auto-show.
autoHidden.add(player.getUniqueId());
2018-04-26 13:52:49 +02:00
}
}
}
2019-08-03 13:47:12 +02:00
@Override
2018-04-26 13:52:49 +02:00
public void hide(Player player) {
hide(player, false);
2018-04-26 13:52:49 +02:00
}
public void hide(Player player, boolean auto) {
2019-08-03 13:47:12 +02:00
NPCHideEvent event = new NPCHideEvent(this, player, auto);
Bukkit.getServer().getPluginManager().callEvent(event);
2018-04-26 13:52:49 +02:00
if (event.isCancelled()) {
return;
}
2020-04-18 10:16:42 +02:00
if (!shown.contains(player.getUniqueId())) {
throw new IllegalArgumentException("NPC cannot be hidden from player before calling NPC#show first");
}
2018-04-26 13:52:49 +02:00
if (auto) {
2020-04-18 10:16:42 +02:00
if (autoHidden.contains(player.getUniqueId())) {
throw new IllegalStateException("NPC cannot be auto-hidden twice");
2018-04-26 13:52:49 +02:00
}
2020-04-18 10:16:42 +02:00
sendHidePackets(player);
// NPC is auto-hidden now, we will add the UUID to the set.
autoHidden.add(player.getUniqueId());
} else {
// Removing the UUID from the set.
2018-04-26 13:52:49 +02:00
shown.remove(player.getUniqueId());
2020-04-18 10:16:42 +02:00
if (inRangeOf(player)) {
// The player is in range of the NPC, send the packets.
sendHidePackets(player);
2018-04-26 13:52:49 +02:00
} else {
2020-04-18 10:16:42 +02:00
// We don't have to send any packets, just don't let it auto-show again by removing the UUID from the set.
autoHidden.remove(player.getUniqueId());
2018-04-26 13:52:49 +02:00
}
}
}
2019-10-20 10:48:12 +02:00
@Override
public boolean getState(NPCState state) {
return activeStates.contains(state);
}
2019-10-20 10:48:12 +02:00
@Override
public NPC toggleState(NPCState state) {
if (activeStates.contains(state)) {
activeStates.remove(state);
} else {
activeStates.add(state);
2019-10-20 10:48:12 +02:00
}
// Send a new metadata packet to all players that can see the NPC.
for (UUID shownUuid : shown) {
Player player = Bukkit.getPlayer(shownUuid);
if (player != null && isShown(player)) {
sendMetadataPacket(player);
}
}
return this;
}
2020-07-14 15:13:26 +02:00
@Override
public void playAnimation(NPCAnimation animation) {
for (UUID shownUuid : shown) {
Player player = Bukkit.getPlayer(shownUuid);
if (player != null && isShown(player)) {
sendAnimationPacket(player, animation);
}
}
}
@Override
public ItemStack getItem(NPCSlot slot) {
Objects.requireNonNull(slot, "Slot cannot be null");
return items.get(slot);
}
2019-10-20 10:48:12 +02:00
@Override
public NPC setItem(NPCSlot slot, ItemStack item) {
Objects.requireNonNull(slot, "Slot cannot be null");
2019-10-20 10:48:12 +02:00
items.put(slot, item);
2019-10-20 10:48:12 +02:00
for (UUID shownUuid : shown) {
Player player = Bukkit.getPlayer(shownUuid);
if (player != null && isShown(player)) {
sendEquipmentPacket(player, slot, false);
2019-10-20 10:48:12 +02:00
}
}
return this;
}
@Override
public NPC setText(List<String> text) {
uniqueText.clear();
for (UUID shownUuid : shown) {
Player player = Bukkit.getPlayer(shownUuid);
if (player != null && isShown(player)) {
Hologram originalhologram = getPlayerHologram(player);
originalhologram.hide(player); // essentially destroy the hologram
textDisplayHolograms.remove(player.getUniqueId()); // remove the old obj
Hologram hologram = getPlayerHologram(player); // let it regenerate
List<Object> updatePackets = hologram.getUpdatePackets(getPlayerLines(player));
hologram.update(player, updatePackets);
}
}
this.text = text;
return this;
}
@Override
public List<String> getText() {
return text;
}
2018-04-26 13:52:49 +02:00
}