Citizens2/main/src/main/java/net/citizensnpcs/trait/HologramTrait.java

336 lines
10 KiB
Java
Raw Normal View History

2020-06-30 12:17:14 +02:00
package net.citizensnpcs.trait;
2021-06-03 14:36:45 +02:00
import java.util.Collection;
import java.util.Collections;
2020-07-03 09:14:55 +02:00
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
2020-07-03 09:14:55 +02:00
import org.bukkit.ChatColor;
2020-07-03 09:14:55 +02:00
import org.bukkit.Location;
import org.bukkit.Material;
2021-06-03 14:36:45 +02:00
import org.bukkit.entity.ArmorStand;
2020-06-30 12:17:14 +02:00
import org.bukkit.entity.EntityType;
2020-07-03 09:14:55 +02:00
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.inventory.ItemStack;
2020-07-03 09:14:55 +02:00
2021-06-03 14:36:45 +02:00
import com.google.common.collect.Collections2;
2020-07-03 09:14:55 +02:00
import com.google.common.collect.Lists;
2020-06-30 12:17:14 +02:00
2020-07-03 09:14:55 +02:00
import net.citizensnpcs.Settings.Setting;
2020-06-30 12:17:14 +02:00
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.MemoryNPCDataStore;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.npc.NPCRegistry;
import net.citizensnpcs.api.persistence.Persist;
import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.trait.TraitName;
import net.citizensnpcs.api.util.Colorizer;
2021-07-04 07:07:15 +02:00
import net.citizensnpcs.api.util.Messaging;
2020-06-30 12:17:14 +02:00
import net.citizensnpcs.api.util.Placeholders;
2022-08-12 07:48:54 +02:00
import net.citizensnpcs.api.util.SpigotUtil;
2020-07-06 02:42:46 +02:00
import net.citizensnpcs.util.NMS;
2020-06-30 12:17:14 +02:00
/**
* Persists a hologram attached to the NPC.
*/
@TraitName("hologramtrait")
public class HologramTrait extends Trait {
2020-07-03 09:14:55 +02:00
private Location currentLoc;
2020-07-27 11:34:07 +02:00
@Persist
private HologramDirection direction = HologramDirection.BOTTOM_UP;
2022-04-21 09:25:57 +02:00
private double lastEntityHeight = 0;
private boolean lastNameplateVisible;
2020-06-30 12:17:14 +02:00
@Persist
2020-07-03 09:14:55 +02:00
private double lineHeight = -1;
2021-09-05 08:59:19 +02:00
private final List<NPC> lineHolograms = Lists.newArrayList();
2020-07-03 09:14:55 +02:00
@Persist
private final List<String> lines = Lists.newArrayList();
private NPC nameNPC;
2021-02-04 03:10:48 +01:00
private final NPCRegistry registry = CitizensAPI.createCitizensBackedNPCRegistry(new MemoryNPCDataStore());
2020-06-30 12:17:14 +02:00
public HologramTrait() {
super("hologramtrait");
}
2021-06-03 14:36:45 +02:00
/**
* Adds a new hologram line which will displayed over an NPC's head.
*
* @param text
* The new line to add
*/
2020-07-03 09:14:55 +02:00
public void addLine(String text) {
lines.add(text);
reloadLineHolograms();
2020-07-03 09:14:55 +02:00
}
2021-06-03 14:36:45 +02:00
/**
* Clears all hologram lines
*/
2020-07-06 19:07:01 +02:00
public void clear() {
lines.clear();
reloadLineHolograms();
2020-07-06 19:07:01 +02:00
}
private NPC createHologram(String line, double heightOffset) {
NPC hologramNPC = registry.createNPC(EntityType.ARMOR_STAND, line);
hologramNPC.addTrait(new ClickRedirectTrait(npc));
hologramNPC.getOrAddTrait(ArmorStandTrait.class).setAsPointEntityWithName();
2020-07-27 11:34:07 +02:00
hologramNPC.spawn(currentLoc.clone().add(0,
getEntityHeight()
+ (direction == HologramDirection.BOTTOM_UP ? heightOffset : getMaxHeight() - heightOffset),
0));
Matcher itemMatcher = ITEM_MATCHER.matcher(line);
if (itemMatcher.matches()) {
2022-08-12 07:48:54 +02:00
Material item = SpigotUtil.isUsing1_13API() ? Material.matchMaterial(itemMatcher.group(1), false)
: Material.matchMaterial(itemMatcher.group(1));
NPC itemNPC = registry.createNPCUsingItem(EntityType.DROPPED_ITEM, "", new ItemStack(item, 1));
itemNPC.spawn(currentLoc);
((ArmorStand) hologramNPC.getEntity()).addPassenger(itemNPC.getEntity());
itemNPC.addRunnable(new Runnable() {
@Override
public void run() {
if (!itemNPC.isSpawned() || !itemNPC.getEntity().isInsideVehicle()) {
itemNPC.destroy();
}
}
});
}
2022-04-21 09:25:57 +02:00
lastEntityHeight = getEntityHeight();
return hologramNPC;
}
2021-06-03 14:36:45 +02:00
/**
* @return The direction that hologram lines are displayed in
*/
2020-10-07 13:16:41 +02:00
public HologramDirection getDirection() {
return direction;
}
2020-07-06 02:42:46 +02:00
private double getEntityHeight() {
return NMS.getHeight(npc.getEntity());
}
2020-07-03 09:14:55 +02:00
private double getHeight(int lineNumber) {
2022-08-12 07:48:54 +02:00
return getLineHeight() * (lastNameplateVisible ? lineNumber + 1 : lineNumber);
2020-07-03 09:14:55 +02:00
}
2021-06-03 14:36:45 +02:00
/**
* Note: this is implementation-specific and may be removed at a later date.
*/
public Collection<ArmorStand> getHologramEntities() {
2021-09-05 08:59:19 +02:00
return Collections2.transform(lineHolograms, (n) -> (ArmorStand) n.getEntity());
2021-06-03 14:36:45 +02:00
}
/**
* @return The line height between each hologram line, in blocks
*/
2020-10-07 13:16:41 +02:00
public double getLineHeight() {
2022-08-12 07:48:54 +02:00
return lineHeight == -1 ? Setting.DEFAULT_NPC_HOLOGRAM_LINE_HEIGHT.asDouble() : lineHeight;
2020-10-07 13:16:41 +02:00
}
2021-06-03 14:36:45 +02:00
/**
* @return the hologram lines, in bottom-up order
*/
2020-07-03 09:14:55 +02:00
public List<String> getLines() {
return Collections.unmodifiableList(lines);
2020-07-03 09:14:55 +02:00
}
2020-07-27 11:34:07 +02:00
private double getMaxHeight() {
2022-08-12 07:48:54 +02:00
return getLineHeight() * (lines.size() + (lastNameplateVisible ? 1 : -1));
2020-07-27 11:34:07 +02:00
}
2021-06-03 14:36:45 +02:00
/**
* Note: this is implementation-specific and may be removed at a later date.
*/
public ArmorStand getNameEntity() {
return nameNPC != null && nameNPC.isSpawned() ? ((ArmorStand) nameNPC.getEntity()) : null;
2021-06-03 14:36:45 +02:00
}
2020-06-30 12:17:14 +02:00
@Override
public void onDespawn() {
2021-07-04 07:07:15 +02:00
if (nameNPC != null) {
nameNPC.destroy();
nameNPC = null;
}
2021-09-05 08:59:19 +02:00
for (NPC npc : lineHolograms) {
2021-07-04 07:07:15 +02:00
npc.destroy();
}
2021-09-05 08:59:19 +02:00
lineHolograms.clear();
2020-06-30 12:17:14 +02:00
}
@Override
public void onRemove() {
2021-07-04 07:07:15 +02:00
onDespawn();
}
2020-06-30 12:17:14 +02:00
@Override
public void onSpawn() {
if (!npc.isSpawned())
return;
lastNameplateVisible = Boolean
.parseBoolean(npc.data().<Object> get(NPC.Metadata.NAMEPLATE_VISIBLE, true).toString());
2021-07-04 07:07:15 +02:00
currentLoc = npc.getStoredLocation();
if (npc.requiresNameHologram() && lastNameplateVisible) {
2021-07-04 07:07:15 +02:00
nameNPC = createHologram(npc.getFullName(), 0);
}
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
lineHolograms.add(createHologram(Placeholders.replace(line, null, npc), getHeight(i)));
}
}
private void reloadLineHolograms() {
for (NPC npc : lineHolograms) {
npc.destroy();
}
lineHolograms.clear();
if (!npc.isSpawned())
return;
2021-07-04 07:07:15 +02:00
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
2021-09-05 08:59:19 +02:00
lineHolograms.add(createHologram(Placeholders.replace(line, null, npc), getHeight(i)));
2021-07-04 07:07:15 +02:00
}
2020-07-03 09:14:55 +02:00
}
2021-06-03 14:36:45 +02:00
/**
* Removes the line at the specified index
*
* @param idx
*/
2020-07-03 09:14:55 +02:00
public void removeLine(int idx) {
if (idx < 0 || idx >= lines.size())
return;
2020-07-03 09:14:55 +02:00
lines.remove(idx);
reloadLineHolograms();
2020-06-30 12:17:14 +02:00
}
@Override
public void run() {
2020-07-03 09:14:55 +02:00
if (!npc.isSpawned()) {
2021-07-04 07:07:15 +02:00
onDespawn();
2020-06-30 12:17:14 +02:00
return;
}
2021-09-05 08:59:19 +02:00
if (currentLoc == null) {
currentLoc = npc.getStoredLocation();
}
boolean nameplateVisible = Boolean
.parseBoolean(npc.data().<Object> get(NPC.Metadata.NAMEPLATE_VISIBLE, true).toString());
2020-07-10 06:19:29 +02:00
if (npc.requiresNameHologram()) {
if (nameNPC != null && !nameplateVisible) {
2020-07-10 06:19:29 +02:00
nameNPC.destroy();
nameNPC = null;
} else if (nameNPC == null && nameplateVisible) {
2020-07-10 06:19:29 +02:00
nameNPC = createHologram(npc.getFullName(), 0);
}
}
2020-11-03 07:36:44 +01:00
boolean update = currentLoc.getWorld() != npc.getStoredLocation().getWorld()
2022-04-21 09:25:57 +02:00
|| currentLoc.distance(npc.getStoredLocation()) >= 0.001 || lastNameplateVisible != nameplateVisible
2022-04-21 09:27:46 +02:00
|| Math.abs(lastEntityHeight - getEntityHeight()) >= 0.05;
lastNameplateVisible = nameplateVisible;
2020-07-03 09:14:55 +02:00
if (update) {
currentLoc = npc.getStoredLocation();
2022-04-21 09:25:57 +02:00
lastEntityHeight = getEntityHeight();
2020-07-03 09:14:55 +02:00
}
if (nameNPC != null && nameNPC.isSpawned()) {
if (update) {
nameNPC.teleport(currentLoc.clone().add(0, getEntityHeight(), 0), TeleportCause.PLUGIN);
}
nameNPC.setName(npc.getFullName());
}
2021-09-05 08:59:19 +02:00
for (int i = 0; i < lineHolograms.size(); i++) {
NPC hologramNPC = lineHolograms.get(i);
if (!hologramNPC.isSpawned())
2020-07-03 09:14:55 +02:00
continue;
2020-07-03 09:14:55 +02:00
if (update) {
2022-08-12 07:48:54 +02:00
hologramNPC.teleport(currentLoc.clone().add(0, lastEntityHeight
+ (direction == HologramDirection.BOTTOM_UP ? getHeight(i) : getMaxHeight() - getHeight(i)), 0),
2020-07-03 09:14:55 +02:00
TeleportCause.PLUGIN);
}
2021-07-04 07:07:15 +02:00
if (i >= lines.size()) {
2021-07-16 11:23:36 +02:00
Messaging.severe("More hologram NPCs than lines for ID", npc.getId(), "lines", lines);
2021-07-04 07:07:15 +02:00
break;
}
2020-07-03 09:14:55 +02:00
String text = lines.get(i);
if (ITEM_MATCHER.matcher(text).matches()) {
text = null;
}
if (text != null && !ChatColor.stripColor(Colorizer.parseColors(text)).isEmpty()) {
2020-07-03 09:14:55 +02:00
hologramNPC.setName(Placeholders.replace(text, null, npc));
hologramNPC.data().set(NPC.Metadata.NAMEPLATE_VISIBLE, true);
2020-07-03 09:14:55 +02:00
} else {
2020-11-07 08:10:49 +01:00
hologramNPC.setName("");
hologramNPC.data().set(NPC.Metadata.NAMEPLATE_VISIBLE, "hover");
2020-07-03 09:14:55 +02:00
}
2020-06-30 12:17:14 +02:00
}
}
2021-06-03 14:36:45 +02:00
/**
* @see #getDirection()
* @param direction
* The new direction
*/
2020-07-27 11:34:07 +02:00
public void setDirection(HologramDirection direction) {
this.direction = direction;
reloadLineHolograms();
2020-07-27 11:34:07 +02:00
}
2021-06-03 14:36:45 +02:00
/**
* Sets the hologram line at a specific index
*
* @param idx
* The index
* @param text
* The new line
*/
2020-07-03 09:14:55 +02:00
public void setLine(int idx, String text) {
2020-08-11 16:57:32 +02:00
if (idx == lines.size()) {
addLine(text);
return;
2020-08-11 16:57:32 +02:00
}
lines.set(idx, text);
if (idx < lineHolograms.size()) {
lineHolograms.get(idx).setName(Placeholders.replace(text, null, npc));
return;
}
reloadLineHolograms();
2020-07-03 09:14:55 +02:00
}
2021-06-03 14:36:45 +02:00
/**
* Sets the line height
*
* @see #getLineHeight()
* @param height
* The line height in blocks
*/
2020-07-03 09:14:55 +02:00
public void setLineHeight(double height) {
lineHeight = height;
reloadLineHolograms();
2020-07-03 09:14:55 +02:00
}
2020-07-27 11:34:07 +02:00
public enum HologramDirection {
BOTTOM_UP,
TOP_DOWN;
}
private static final Pattern ITEM_MATCHER = Pattern.compile("<item:(.*?)>");
2020-06-30 12:17:14 +02:00
}