package net.citizensnpcs.trait;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Display.Billboard;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.entity.TextDisplay;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Transformation;
import org.joml.Vector3d;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.command.Arg.CompletionsProvider;
import net.citizensnpcs.api.command.CommandContext;
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.DataKey;
import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.api.util.Placeholders;
import net.citizensnpcs.api.util.SpigotUtil;
import net.citizensnpcs.util.NMS;
import net.citizensnpcs.util.Util;
/**
* Manages a set of holograms attached to the NPC. Holograms are lines of text or items that follow the NPC at
* some offset (typically vertically offset).
*/
// TODO: cleanup
@TraitName("hologramtrait")
public class HologramTrait extends Trait {
private Location currentLoc;
private double lastEntityBbHeight = 0;
private boolean lastNameplateVisible;
@Persist
private double lineHeight = -1;
private final List lines = Lists.newArrayList();
private HologramLine nameLine;
private final NPCRegistry registry = CitizensAPI.createCitizensBackedNPCRegistry(new MemoryNPCDataStore());
private int t;
@Persist
private int viewRange = -1;
public HologramTrait() {
super("hologramtrait");
}
/**
* Adds a new hologram line which will displayed over an NPC's head.
*
* @param text
* The new line to add
*/
public void addLine(String text) {
lines.add(new HologramLine(text, true));
reloadLineHolograms();
}
public void addLine(String text, HologramRenderer hr) {
lines.add(new HologramLine(text, hr));
reloadLineHolograms();
}
/**
* Adds a new hologram line which will displayed over an NPC's head. It will not persist to disk and will last for
* the specified amount of ticks.
*
* @param text
* The new line to add
* @param ticks
* The number of ticks to last for
*/
public void addTemporaryLine(String text, int ticks) {
lines.add(new HologramLine(text, false, ticks));
reloadLineHolograms();
}
/**
* Clears all hologram lines
*/
public void clear() {
for (HologramLine line : lines) {
line.removeNPC();
}
lines.clear();
}
private double getEntityBbHeight() {
return NMS.getBoundingBoxHeight(npc.getEntity());
}
private double getHeight(int lineNumber) {
double base = lastNameplateVisible ? 0 : -getLineHeight();
for (int i = 0; i <= lineNumber; i++) {
HologramLine line = lines.get(i);
base += line.mb + getLineHeight();
if (i != lineNumber) {
base += line.mt;
}
}
return base;
}
/**
* @return The line height between each hologram line, in blocks
*/
public double getLineHeight() {
return lineHeight == -1 ? Setting.DEFAULT_NPC_HOLOGRAM_LINE_HEIGHT.asDouble() : lineHeight;
}
/**
* @return the hologram lines, in bottom-up order
*/
public List getLines() {
return Lists.transform(lines, l -> l.text);
}
public int getViewRange() {
return viewRange;
}
@Override
public void load(DataKey root) {
clear();
for (DataKey key : root.getRelative("lines").getIntegerSubKeys()) {
HologramLine line = new HologramLine(key.keyExists("text") ? key.getString("text") : key.getString(""),
true);
line.mt = key.keyExists("margin.top") ? key.getDouble("margin.top") : 0.0;
line.mb = key.keyExists("margin.bottom") ? key.getDouble("margin.bottom") : 0.0;
lines.add(line);
}
}
@Override
public void onDespawn() {
reloadLineHolograms();
}
@Override
public void onRemove() {
onDespawn();
}
@Override
public void onSpawn() {
if (!npc.isSpawned())
return;
lastNameplateVisible = Boolean
.parseBoolean(npc.data().