2020-06-30 12:17:14 +02:00
|
|
|
package net.citizensnpcs.trait;
|
|
|
|
|
2021-06-03 14:36:45 +02:00
|
|
|
import java.util.Collection;
|
2023-10-30 15:49:02 +01:00
|
|
|
import java.util.Collections;
|
2020-07-03 09:14:55 +02:00
|
|
|
import java.util.List;
|
2024-04-14 19:42:09 +02:00
|
|
|
import java.util.Set;
|
2021-12-27 16:59:21 +01:00
|
|
|
import java.util.regex.Matcher;
|
|
|
|
import java.util.regex.Pattern;
|
2022-10-16 04:41:34 +02:00
|
|
|
import java.util.stream.Collectors;
|
2023-10-30 15:49:02 +01:00
|
|
|
import java.util.stream.IntStream;
|
2020-07-03 09:14:55 +02:00
|
|
|
|
2023-07-19 17:53:43 +02:00
|
|
|
import org.bukkit.Bukkit;
|
2020-11-17 04:39:24 +01:00
|
|
|
import org.bukkit.ChatColor;
|
2020-07-03 09:14:55 +02:00
|
|
|
import org.bukkit.Location;
|
2021-12-27 16:59:21 +01:00
|
|
|
import org.bukkit.Material;
|
2023-10-30 15:49:02 +01:00
|
|
|
import org.bukkit.command.CommandSender;
|
2024-04-14 19:42:09 +02:00
|
|
|
import org.bukkit.entity.Display.Billboard;
|
2023-03-14 20:37:05 +01:00
|
|
|
import org.bukkit.entity.Entity;
|
2020-06-30 12:17:14 +02:00
|
|
|
import org.bukkit.entity.EntityType;
|
2023-04-23 17:46:20 +02:00
|
|
|
import org.bukkit.entity.Player;
|
2024-04-14 19:42:09 +02:00
|
|
|
import org.bukkit.entity.TextDisplay;
|
2020-07-03 09:14:55 +02:00
|
|
|
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
2021-12-27 16:59:21 +01:00
|
|
|
import org.bukkit.inventory.ItemStack;
|
2024-04-14 19:42:09 +02:00
|
|
|
import org.bukkit.util.Transformation;
|
2024-03-16 16:10:12 +01:00
|
|
|
import org.joml.Vector3d;
|
2020-07-03 09:14:55 +02:00
|
|
|
|
2024-04-14 19:42:09 +02:00
|
|
|
import com.google.common.collect.ImmutableSet;
|
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;
|
2023-10-30 15:49:02 +01:00
|
|
|
import net.citizensnpcs.api.command.Arg.CompletionsProvider;
|
|
|
|
import net.citizensnpcs.api.command.CommandContext;
|
2020-06-30 12:17:14 +02:00
|
|
|
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;
|
2022-09-03 18:22:20 +02:00
|
|
|
import net.citizensnpcs.api.util.DataKey;
|
2024-04-14 19:42:09 +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;
|
2022-10-16 04:41:34 +02:00
|
|
|
import net.citizensnpcs.util.Util;
|
2020-06-30 12:17:14 +02:00
|
|
|
|
|
|
|
/**
|
2023-10-29 18:37:00 +01:00
|
|
|
* Manages a set of <em>holograms</em> attached to the NPC. Holograms are lines of text or items that follow the NPC at
|
|
|
|
* some offset (typically vertically offset).
|
2020-06-30 12:17:14 +02:00
|
|
|
*/
|
2024-04-14 19:42:09 +02:00
|
|
|
// TODO: cleanup
|
2020-06-30 12:17:14 +02:00
|
|
|
@TraitName("hologramtrait")
|
|
|
|
public class HologramTrait extends Trait {
|
2020-07-03 09:14:55 +02:00
|
|
|
private Location currentLoc;
|
2023-12-01 16:43:58 +01:00
|
|
|
private double lastEntityBbHeight = 0;
|
2021-09-21 10:12:15 +02:00
|
|
|
private boolean lastNameplateVisible;
|
2020-06-30 12:17:14 +02:00
|
|
|
@Persist
|
2020-07-03 09:14:55 +02:00
|
|
|
private double lineHeight = -1;
|
2022-09-03 18:22:20 +02:00
|
|
|
private final List<HologramLine> lines = Lists.newArrayList();
|
2023-04-24 11:55:04 +02:00
|
|
|
private HologramLine nameLine;
|
2021-02-04 03:10:48 +01:00
|
|
|
private final NPCRegistry registry = CitizensAPI.createCitizensBackedNPCRegistry(new MemoryNPCDataStore());
|
2023-02-20 16:34:12 +01:00
|
|
|
private int t;
|
2023-11-05 13:58:37 +01:00
|
|
|
@Persist
|
|
|
|
private int viewRange = -1;
|
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) {
|
2022-09-03 18:22:20 +02:00
|
|
|
lines.add(new HologramLine(text, true));
|
2022-04-24 20:22:39 +02:00
|
|
|
reloadLineHolograms();
|
2020-07-03 09:14:55 +02:00
|
|
|
}
|
|
|
|
|
2024-04-14 19:42:09 +02:00
|
|
|
public void addLine(String text, HologramRenderer hr) {
|
|
|
|
lines.add(new HologramLine(text, hr));
|
|
|
|
reloadLineHolograms();
|
|
|
|
}
|
|
|
|
|
2022-09-03 18:22:20 +02:00
|
|
|
/**
|
|
|
|
* 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));
|
2022-10-16 04:41:34 +02:00
|
|
|
reloadLineHolograms();
|
2022-09-03 18:22:20 +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() {
|
2022-10-16 04:41:34 +02:00
|
|
|
for (HologramLine line : lines) {
|
|
|
|
line.removeNPC();
|
|
|
|
}
|
2020-07-06 19:07:01 +02:00
|
|
|
lines.clear();
|
|
|
|
}
|
|
|
|
|
2023-12-01 16:43:58 +01:00
|
|
|
private double getEntityBbHeight() {
|
|
|
|
return NMS.getBoundingBoxHeight(npc.getEntity());
|
2020-07-06 02:42:46 +02:00
|
|
|
}
|
|
|
|
|
2020-07-03 09:14:55 +02:00
|
|
|
private double getHeight(int lineNumber) {
|
2023-11-05 13:58:37 +01:00
|
|
|
double base = lastNameplateVisible ? 0 : -getLineHeight();
|
2023-02-14 16:19:45 +01:00
|
|
|
for (int i = 0; i <= lineNumber; i++) {
|
|
|
|
HologramLine line = lines.get(i);
|
|
|
|
base += line.mb + getLineHeight();
|
|
|
|
if (i != lineNumber) {
|
|
|
|
base += line.mt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return base;
|
2020-07-03 09:14:55 +02:00
|
|
|
}
|
|
|
|
|
2024-04-14 19:56:10 +02:00
|
|
|
public Collection<Entity> getHologramEntities() {
|
|
|
|
return lines.stream().filter(l -> l.renderer.getEntity() != null).map(l -> l.renderer.getEntity())
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
}
|
|
|
|
|
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() {
|
2023-07-29 17:26:02 +02:00
|
|
|
return Lists.transform(lines, l -> l.text);
|
2020-07-03 09:14:55 +02:00
|
|
|
}
|
|
|
|
|
2024-04-14 19:56:10 +02:00
|
|
|
public Entity getNameEntity() {
|
|
|
|
return nameLine == null ? null : nameLine.renderer.getEntity();
|
|
|
|
}
|
|
|
|
|
2024-04-14 19:42:09 +02:00
|
|
|
public int getViewRange() {
|
2023-11-05 13:58:37 +01:00
|
|
|
return viewRange;
|
|
|
|
}
|
|
|
|
|
2022-09-03 18:22:20 +02:00
|
|
|
@Override
|
|
|
|
public void load(DataKey root) {
|
2022-10-16 04:41:34 +02:00
|
|
|
clear();
|
2022-09-03 18:22:20 +02:00
|
|
|
for (DataKey key : root.getRelative("lines").getIntegerSubKeys()) {
|
2023-06-25 15:58:09 +02:00
|
|
|
HologramLine line = new HologramLine(key.keyExists("text") ? key.getString("text") : key.getString(""),
|
|
|
|
true);
|
2023-06-17 18:08:44 +02:00
|
|
|
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);
|
2022-09-03 18:22:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-30 12:17:14 +02:00
|
|
|
@Override
|
|
|
|
public void onDespawn() {
|
2024-04-14 19:42:09 +02:00
|
|
|
reloadLineHolograms();
|
2023-10-29 18:37:00 +01:00
|
|
|
}
|
|
|
|
|
2020-07-03 16:59:18 +02:00
|
|
|
@Override
|
|
|
|
public void onRemove() {
|
2021-07-04 07:07:15 +02:00
|
|
|
onDespawn();
|
2020-07-03 16:59:18 +02:00
|
|
|
}
|
|
|
|
|
2020-06-30 12:17:14 +02:00
|
|
|
@Override
|
|
|
|
public void onSpawn() {
|
2021-08-27 11:30:27 +02:00
|
|
|
if (!npc.isSpawned())
|
|
|
|
return;
|
2022-10-16 04:41:34 +02:00
|
|
|
|
2021-09-21 10:12:15 +02:00
|
|
|
lastNameplateVisible = Boolean
|
2022-03-23 13:57:18 +01:00
|
|
|
.parseBoolean(npc.data().<Object> get(NPC.Metadata.NAMEPLATE_VISIBLE, true).toString());
|
2022-04-24 20:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void reloadLineHolograms() {
|
2022-10-16 04:41:34 +02:00
|
|
|
for (HologramLine line : lines) {
|
|
|
|
line.removeNPC();
|
2022-04-24 20:22:39 +02:00
|
|
|
}
|
2024-04-14 19:42:09 +02:00
|
|
|
if (nameLine != null) {
|
|
|
|
nameLine.removeNPC();
|
|
|
|
nameLine = null;
|
2021-07-04 07:07:15 +02:00
|
|
|
}
|
2024-04-14 19:42:09 +02:00
|
|
|
currentLoc = null;
|
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) {
|
2022-04-24 20:22:39 +02:00
|
|
|
if (idx < 0 || idx >= lines.size())
|
|
|
|
return;
|
|
|
|
|
2022-10-16 04:41:34 +02:00
|
|
|
lines.remove(idx).removeNPC();
|
2022-04-24 20:22:39 +02:00
|
|
|
|
|
|
|
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-21 10:12:15 +02:00
|
|
|
boolean nameplateVisible = Boolean
|
2022-03-23 13:57:18 +01:00
|
|
|
.parseBoolean(npc.data().<Object> get(NPC.Metadata.NAMEPLATE_VISIBLE, true).toString());
|
2020-07-10 06:19:29 +02:00
|
|
|
if (npc.requiresNameHologram()) {
|
2023-04-24 11:55:04 +02:00
|
|
|
if (nameLine != null && !nameplateVisible) {
|
|
|
|
nameLine.removeNPC();
|
|
|
|
nameLine = null;
|
|
|
|
} else if (nameLine == null && nameplateVisible) {
|
2024-04-14 19:42:09 +02:00
|
|
|
nameLine = new HologramLine(npc.getRawName(),
|
|
|
|
SUPPORTS_DISPLAY ? new InteractionVehicleRenderer() : new ArmorstandVehicleRenderer());
|
2020-07-10 06:19:29 +02:00
|
|
|
}
|
|
|
|
}
|
2023-10-22 19:58:06 +02:00
|
|
|
Location npcLoc = npc.getStoredLocation();
|
2024-04-14 19:42:09 +02:00
|
|
|
Vector3d offset = new Vector3d();
|
|
|
|
boolean updatePosition = Setting.HOLOGRAM_ALWAYS_UPDATE_POSITION.asBoolean() || currentLoc == null
|
2023-10-22 19:58:06 +02:00
|
|
|
|| currentLoc.getWorld() != npcLoc.getWorld() || currentLoc.distance(npcLoc) >= 0.001
|
2023-12-01 16:43:58 +01:00
|
|
|
|| lastNameplateVisible != nameplateVisible
|
|
|
|
|| Math.abs(lastEntityBbHeight - getEntityBbHeight()) >= 0.05;
|
2023-02-20 16:34:12 +01:00
|
|
|
boolean updateName = false;
|
2023-10-22 19:58:06 +02:00
|
|
|
|
2023-03-14 18:41:24 +01:00
|
|
|
if (t++ >= Setting.HOLOGRAM_UPDATE_RATE.asTicks() + Util.getFastRandom().nextInt(3) /* add some jitter */) {
|
2023-02-20 16:34:12 +01:00
|
|
|
t = 0;
|
|
|
|
updateName = true;
|
|
|
|
}
|
2021-09-21 10:12:15 +02:00
|
|
|
lastNameplateVisible = nameplateVisible;
|
|
|
|
|
2022-10-16 04:41:34 +02:00
|
|
|
if (updatePosition) {
|
2023-10-22 19:58:06 +02:00
|
|
|
currentLoc = npcLoc.clone();
|
2023-12-01 16:43:58 +01:00
|
|
|
lastEntityBbHeight = getEntityBbHeight();
|
2020-07-03 09:14:55 +02:00
|
|
|
}
|
2024-04-14 19:42:09 +02:00
|
|
|
if (nameLine != null) {
|
|
|
|
if (updatePosition) {
|
|
|
|
nameLine.render(offset);
|
2023-10-22 19:58:06 +02:00
|
|
|
}
|
2023-02-20 16:34:12 +01:00
|
|
|
if (updateName) {
|
2023-04-24 11:55:04 +02:00
|
|
|
nameLine.setText(npc.getRawName());
|
2023-02-20 16:34:12 +01:00
|
|
|
}
|
2020-07-06 10:37:34 +02:00
|
|
|
}
|
2022-10-16 04:41:34 +02:00
|
|
|
for (int i = 0; i < lines.size(); i++) {
|
|
|
|
HologramLine line = lines.get(i);
|
2024-03-02 15:29:05 +01:00
|
|
|
|
2023-10-22 19:58:06 +02:00
|
|
|
if (line.ticks > 0 && --line.ticks == 0) {
|
2024-03-16 16:10:12 +01:00
|
|
|
lines.remove(i--).removeNPC();
|
2023-10-22 19:58:06 +02:00
|
|
|
continue;
|
|
|
|
}
|
2024-04-14 19:42:09 +02:00
|
|
|
if (updatePosition) {
|
|
|
|
offset.y = getHeight(i);
|
|
|
|
line.render(offset);
|
2023-10-31 17:58:28 +01:00
|
|
|
}
|
2024-04-14 19:42:09 +02:00
|
|
|
if (updateName) {
|
|
|
|
line.setText(line.text);
|
2023-11-05 13:58:37 +01:00
|
|
|
}
|
2020-06-30 12:17:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-03 18:22:20 +02:00
|
|
|
@Override
|
|
|
|
public void save(DataKey root) {
|
|
|
|
root.removeKey("lines");
|
|
|
|
int i = 0;
|
|
|
|
for (HologramLine line : lines) {
|
2024-03-16 16:10:12 +01:00
|
|
|
if (!line.persist)
|
2022-09-03 18:22:20 +02:00
|
|
|
continue;
|
2024-03-16 16:10:12 +01:00
|
|
|
|
2023-06-17 18:08:44 +02:00
|
|
|
root.setString("lines." + i + ".text", line.text);
|
|
|
|
root.setDouble("lines." + i + ".margin.top", line.mt);
|
|
|
|
root.setDouble("lines." + i + ".margin.bottom", line.mb);
|
2022-09-03 18:22:20 +02:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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()) {
|
2022-04-24 20:22:39 +02:00
|
|
|
addLine(text);
|
|
|
|
return;
|
2020-08-11 16:57:32 +02:00
|
|
|
}
|
2022-10-16 04:41:34 +02:00
|
|
|
HologramLine line = lines.get(idx);
|
|
|
|
line.setText(text);
|
2024-04-14 19:42:09 +02:00
|
|
|
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;
|
2022-04-24 20:22:39 +02:00
|
|
|
reloadLineHolograms();
|
2020-07-03 09:14:55 +02:00
|
|
|
}
|
2020-07-27 11:34:07 +02:00
|
|
|
|
2023-06-17 18:08:44 +02:00
|
|
|
/**
|
|
|
|
* Sets the margin of a line at a specific index
|
|
|
|
*
|
|
|
|
* @param idx
|
2024-04-14 19:42:09 +02:00
|
|
|
* The line index
|
2023-06-17 18:08:44 +02:00
|
|
|
* @param type
|
|
|
|
* The margin type, top or bottom
|
|
|
|
* @param margin
|
|
|
|
* The margin
|
|
|
|
*/
|
|
|
|
public void setMargin(int idx, String type, double margin) {
|
|
|
|
if (type.equalsIgnoreCase("top")) {
|
|
|
|
lines.get(idx).mt = margin;
|
2023-06-25 15:58:09 +02:00
|
|
|
} else if (type.equalsIgnoreCase("bottom")) {
|
2023-06-17 18:08:44 +02:00
|
|
|
lines.get(idx).mb = margin;
|
|
|
|
}
|
|
|
|
reloadLineHolograms();
|
|
|
|
}
|
|
|
|
|
2023-11-05 13:58:37 +01:00
|
|
|
public void setViewRange(int range) {
|
|
|
|
this.viewRange = range;
|
2023-03-20 10:28:17 +01:00
|
|
|
reloadLineHolograms();
|
2023-03-19 09:14:53 +01:00
|
|
|
}
|
|
|
|
|
2024-04-14 19:42:09 +02:00
|
|
|
public class ArmorstandRenderer extends SingleEntityHologramRenderer {
|
2024-03-16 16:10:12 +01:00
|
|
|
@Override
|
2024-04-14 19:42:09 +02:00
|
|
|
protected NPC createNPC(Entity base, String name, Vector3d offset) {
|
|
|
|
NPC npc = registry.createNPC(EntityType.ARMOR_STAND, name);
|
|
|
|
npc.getOrAddTrait(ArmorStandTrait.class).setAsHelperEntityWithName(npc);
|
|
|
|
return npc;
|
2024-03-16 16:10:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2024-04-14 19:42:09 +02:00
|
|
|
protected void render0(NPC npc, Vector3d offset) {
|
2024-04-14 19:48:17 +02:00
|
|
|
hologram.getEntity().teleport(
|
|
|
|
npc.getStoredLocation().clone().add(offset.x, offset.y + getEntityBbHeight(), offset.z),
|
2024-03-16 16:10:12 +01:00
|
|
|
TeleportCause.PLUGIN);
|
|
|
|
}
|
2024-04-14 19:42:09 +02:00
|
|
|
}
|
2024-03-16 16:10:12 +01:00
|
|
|
|
2024-04-14 19:42:09 +02:00
|
|
|
public class ArmorstandVehicleRenderer extends ArmorstandRenderer {
|
2024-03-16 16:10:12 +01:00
|
|
|
@Override
|
2024-04-14 19:42:09 +02:00
|
|
|
protected NPC createNPC(Entity base, String name, Vector3d offset) {
|
|
|
|
NPC npc = registry.createNPC(EntityType.ARMOR_STAND, name);
|
|
|
|
npc.getOrAddTrait(ArmorStandTrait.class).setAsHelperEntityWithName(npc);
|
|
|
|
return npc;
|
2024-03-16 16:10:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2024-04-14 19:42:09 +02:00
|
|
|
public void render0(NPC base, Vector3d offset) {
|
|
|
|
if (hologram.getEntity().getVehicle() == null) {
|
|
|
|
base.getEntity().addPassenger(hologram.getEntity());
|
|
|
|
}
|
2024-03-16 16:10:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-14 19:42:09 +02:00
|
|
|
private class HologramLine {
|
2023-02-14 16:19:45 +01:00
|
|
|
double mb, mt;
|
2022-09-03 18:22:20 +02:00
|
|
|
boolean persist;
|
2024-03-16 16:10:12 +01:00
|
|
|
HologramRenderer renderer;
|
2022-09-03 18:22:20 +02:00
|
|
|
String text;
|
2022-10-16 04:41:34 +02:00
|
|
|
int ticks;
|
2022-09-03 18:22:20 +02:00
|
|
|
|
|
|
|
public HologramLine(String text, boolean persist) {
|
2024-04-14 19:42:09 +02:00
|
|
|
this(text, persist, -1,
|
|
|
|
SUPPORTS_DISPLAY && Setting.DEFAULT_HOLOGRAM_RENDERER.asString().equalsIgnoreCase("interaction")
|
|
|
|
? new InteractionVehicleRenderer()
|
|
|
|
: SUPPORTS_DISPLAY
|
|
|
|
&& Setting.DEFAULT_HOLOGRAM_RENDERER.asString().equalsIgnoreCase("display")
|
|
|
|
? new TextDisplayVehicleRenderer()
|
|
|
|
: new ArmorstandRenderer());
|
2022-09-03 18:22:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public HologramLine(String text, boolean persist, int ticks) {
|
2024-04-14 19:42:09 +02:00
|
|
|
this(text, persist, ticks,
|
|
|
|
SUPPORTS_DISPLAY && Setting.DEFAULT_HOLOGRAM_RENDERER.asString().equalsIgnoreCase("interaction")
|
|
|
|
? new InteractionVehicleRenderer()
|
|
|
|
: SUPPORTS_DISPLAY
|
|
|
|
&& Setting.DEFAULT_HOLOGRAM_RENDERER.asString().equalsIgnoreCase("display")
|
|
|
|
? new TextDisplayVehicleRenderer()
|
|
|
|
: new ArmorstandRenderer());
|
|
|
|
}
|
|
|
|
|
|
|
|
public HologramLine(String text, boolean persist, int ticks, HologramRenderer hr) {
|
2023-02-14 16:19:45 +01:00
|
|
|
if (ITEM_MATCHER.matcher(text).matches()) {
|
|
|
|
mb = 0.21;
|
|
|
|
mt = 0.07;
|
2024-04-14 19:42:09 +02:00
|
|
|
hr = new ItemRenderer();
|
2023-02-14 16:19:45 +01:00
|
|
|
}
|
2024-04-14 19:42:09 +02:00
|
|
|
this.persist = persist;
|
|
|
|
this.ticks = ticks;
|
|
|
|
this.renderer = hr;
|
|
|
|
setText(text);
|
2022-09-03 18:22:20 +02:00
|
|
|
}
|
2022-10-16 04:41:34 +02:00
|
|
|
|
2024-04-14 19:42:09 +02:00
|
|
|
public HologramLine(String text, HologramRenderer renderer) {
|
|
|
|
this(text, false, -1, renderer);
|
2023-04-23 17:46:20 +02:00
|
|
|
}
|
|
|
|
|
2022-10-16 04:41:34 +02:00
|
|
|
public void removeNPC() {
|
2024-04-14 19:42:09 +02:00
|
|
|
renderer.destroy();
|
|
|
|
}
|
2022-10-16 04:41:34 +02:00
|
|
|
|
2024-04-14 19:42:09 +02:00
|
|
|
public void render(Vector3d vector3d) {
|
|
|
|
renderer.render(npc, vector3d);
|
2022-10-16 04:41:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void setText(String text) {
|
2023-04-23 17:46:20 +02:00
|
|
|
this.text = text == null ? "" : text;
|
2024-04-14 19:42:09 +02:00
|
|
|
if (ITEM_MATCHER.matcher(text).matches()) {
|
|
|
|
renderer.destroy();
|
|
|
|
mb = 0.21;
|
|
|
|
mt = 0.07;
|
|
|
|
renderer = new ItemRenderer();
|
2023-04-23 17:46:20 +02:00
|
|
|
}
|
2024-04-14 19:42:09 +02:00
|
|
|
renderer.updateText(npc, text);
|
2022-10-16 04:41:34 +02:00
|
|
|
}
|
2022-09-03 18:22:20 +02:00
|
|
|
}
|
|
|
|
|
2024-03-16 16:10:12 +01:00
|
|
|
public static interface HologramRenderer {
|
|
|
|
void destroy();
|
|
|
|
|
2024-04-14 19:56:10 +02:00
|
|
|
@Deprecated
|
|
|
|
Entity getEntity();
|
|
|
|
|
2024-04-14 19:42:09 +02:00
|
|
|
String getPerPlayerText(NPC npc, Player viewer);
|
2024-03-16 16:10:12 +01:00
|
|
|
|
2024-04-14 19:42:09 +02:00
|
|
|
default boolean isSneaking(NPC npc, Player player) {
|
|
|
|
return NMS.isSneaking(player);
|
|
|
|
}
|
2024-03-16 16:10:12 +01:00
|
|
|
|
2024-04-14 19:42:09 +02:00
|
|
|
default void onSeenByPlayer(Player player) {
|
2024-03-16 16:10:12 +01:00
|
|
|
}
|
|
|
|
|
2024-04-14 19:42:09 +02:00
|
|
|
void render(NPC npc, Vector3d offset);
|
|
|
|
|
|
|
|
void updateText(NPC npc, String text);
|
|
|
|
}
|
|
|
|
|
|
|
|
public class InteractionVehicleRenderer extends SingleEntityHologramRenderer {
|
|
|
|
private Vector3d lastOffset;
|
|
|
|
|
2024-03-16 16:10:12 +01:00
|
|
|
@Override
|
2024-04-14 19:42:09 +02:00
|
|
|
protected NPC createNPC(Entity base, String name, Vector3d offset) {
|
|
|
|
lastOffset = new Vector3d(offset);
|
2024-03-16 16:10:12 +01:00
|
|
|
return registry.createNPC(EntityType.INTERACTION, name);
|
|
|
|
}
|
2024-04-14 19:42:09 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSeenByPlayer(Player player) {
|
|
|
|
if (lastOffset == null)
|
|
|
|
return;
|
|
|
|
NMS.linkTextInteraction(player, hologram.getEntity(), npc.getEntity(), lastOffset.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void render0(NPC npc, Vector3d offset) {
|
|
|
|
lastOffset = new Vector3d(offset);
|
|
|
|
if (hologram.getEntity().getVehicle() == null) {
|
|
|
|
npc.getEntity().addPassenger(hologram.getEntity());
|
|
|
|
}
|
|
|
|
}
|
2024-03-16 16:10:12 +01:00
|
|
|
}
|
|
|
|
|
2024-04-14 19:42:09 +02:00
|
|
|
public class ItemRenderer extends SingleEntityHologramRenderer {
|
2024-03-16 16:10:12 +01:00
|
|
|
@Override
|
2024-04-14 19:42:09 +02:00
|
|
|
protected NPC createNPC(Entity base, String name, Vector3d offset) {
|
2024-03-16 16:10:12 +01:00
|
|
|
Matcher itemMatcher = ITEM_MATCHER.matcher(name);
|
|
|
|
Material item = SpigotUtil.isUsing1_13API() ? Material.matchMaterial(itemMatcher.group(1), false)
|
|
|
|
: Material.matchMaterial(itemMatcher.group(1));
|
|
|
|
ItemStack itemStack = new ItemStack(item, 1);
|
2024-04-14 19:42:09 +02:00
|
|
|
NPC npc = registry.createNPCUsingItem(EntityType.DROPPED_ITEM, "", itemStack);
|
|
|
|
npc.data().setPersistent(NPC.Metadata.NAMEPLATE_VISIBLE, false);
|
2024-03-16 16:10:12 +01:00
|
|
|
if (itemMatcher.group(2) != null) {
|
|
|
|
if (itemMatcher.group(2).charAt(1) == '{') {
|
|
|
|
Bukkit.getUnsafe().modifyItemStack(itemStack, itemMatcher.group(2).substring(1));
|
2024-04-14 19:42:09 +02:00
|
|
|
npc.setItemProvider(() -> itemStack);
|
2024-03-16 16:10:12 +01:00
|
|
|
} else {
|
2024-04-14 19:42:09 +02:00
|
|
|
npc.getOrAddTrait(ScoreboardTrait.class)
|
2024-03-16 16:10:12 +01:00
|
|
|
.setColor(Util.matchEnum(ChatColor.values(), itemMatcher.group(2).substring(1)));
|
|
|
|
}
|
|
|
|
}
|
2024-04-14 19:42:09 +02:00
|
|
|
return npc;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void render0(NPC npc, Vector3d offset) {
|
|
|
|
hologram.getEntity().teleport(npc.getStoredLocation().clone().add(offset.x, offset.y, offset.z),
|
|
|
|
TeleportCause.PLUGIN);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public abstract class SingleEntityHologramRenderer implements HologramRenderer {
|
|
|
|
protected NPC hologram;
|
|
|
|
protected String text;
|
|
|
|
|
|
|
|
protected abstract NPC createNPC(Entity base, String text, Vector3d offset);
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void destroy() {
|
|
|
|
if (hologram != null) {
|
|
|
|
hologram.destroy();
|
|
|
|
hologram = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-14 19:56:10 +02:00
|
|
|
@Override
|
|
|
|
public Entity getEntity() {
|
|
|
|
return hologram != null ? hologram.getEntity() : null;
|
|
|
|
}
|
|
|
|
|
2024-04-14 19:42:09 +02:00
|
|
|
@Override
|
|
|
|
public String getPerPlayerText(NPC npc, Player viewer) {
|
|
|
|
return Placeholders.replace(text, viewer, npc);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void render(NPC npc, Vector3d offset) {
|
|
|
|
if (hologram == null) {
|
|
|
|
spawnHologram(npc, offset);
|
|
|
|
}
|
|
|
|
render0(npc, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected abstract void render0(NPC npc, Vector3d offset);
|
|
|
|
|
|
|
|
protected void spawnHologram(NPC npc, Vector3d offset) {
|
|
|
|
hologram = createNPC(npc.getEntity(), text, offset);
|
|
|
|
if (!hologram.hasTrait(ClickRedirectTrait.class)) {
|
|
|
|
hologram.addTrait(new ClickRedirectTrait(npc));
|
|
|
|
}
|
|
|
|
hologram.data().set(NPC.Metadata.HOLOGRAM_RENDERER, this);
|
|
|
|
if (Setting.PACKET_HOLOGRAMS.asBoolean()) {
|
|
|
|
hologram.addTrait(PacketNPC.class);
|
|
|
|
}
|
|
|
|
if (viewRange != -1) {
|
|
|
|
hologram.data().set(NPC.Metadata.TRACKING_RANGE, viewRange);
|
|
|
|
} else if (npc.data().has(NPC.Metadata.TRACKING_RANGE)) {
|
|
|
|
hologram.data().set(NPC.Metadata.TRACKING_RANGE, npc.data().get(NPC.Metadata.TRACKING_RANGE));
|
|
|
|
}
|
|
|
|
hologram.spawn(npc.getEntity().getLocation().add(offset.x, offset.y, offset.z));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void updateText(NPC npc, String text) {
|
|
|
|
this.text = Placeholders.replace(text, null, npc);
|
|
|
|
if (hologram == null)
|
|
|
|
return;
|
|
|
|
hologram.setName(text);
|
|
|
|
if (!Placeholders.containsPlaceholders(text)) {
|
|
|
|
hologram.data().set(NPC.Metadata.NAMEPLATE_VISIBLE, Messaging.stripColor(text).length() > 0);
|
|
|
|
}
|
2024-03-16 16:10:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-30 15:49:02 +01:00
|
|
|
public static class TabCompletions implements CompletionsProvider {
|
|
|
|
@Override
|
|
|
|
public Collection<String> getCompletions(CommandContext args, CommandSender sender, NPC npc) {
|
2024-03-24 17:39:47 +01:00
|
|
|
if (args.length() > 1 && npc != null && LINE_ARGS.contains(args.getString(1).toLowerCase())) {
|
2023-10-30 15:49:02 +01:00
|
|
|
HologramTrait ht = npc.getOrAddTrait(HologramTrait.class);
|
|
|
|
return IntStream.range(0, ht.getLines().size()).mapToObj(Integer::toString)
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
}
|
|
|
|
return Collections.emptyList();
|
2023-03-20 10:28:17 +01:00
|
|
|
}
|
2023-10-30 15:49:02 +01:00
|
|
|
|
2024-04-14 19:42:09 +02:00
|
|
|
private static Set<String> LINE_ARGS = ImmutableSet.of("set", "remove", "margintop", "marginbottom");
|
2023-03-20 10:28:17 +01:00
|
|
|
}
|
2023-10-30 15:49:02 +01:00
|
|
|
|
2024-04-14 19:42:09 +02:00
|
|
|
public class TextDisplayVehicleRenderer extends SingleEntityHologramRenderer {
|
|
|
|
@Override
|
|
|
|
protected NPC createNPC(Entity base, String name, Vector3d offset) {
|
|
|
|
NPC npc = registry.createNPC(EntityType.TEXT_DISPLAY, name);
|
|
|
|
return npc;
|
2024-03-16 16:10:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2024-04-14 19:42:09 +02:00
|
|
|
public void render0(NPC base, Vector3d offset) {
|
|
|
|
TextDisplay disp = (TextDisplay) hologram.getEntity();
|
|
|
|
disp.setBillboard(Billboard.CENTER);
|
|
|
|
Transformation tf = disp.getTransformation();
|
|
|
|
tf.getTranslation().y = (float) offset.y + 0.1f;
|
|
|
|
disp.setTransformation(tf);
|
|
|
|
if (hologram.getEntity().getVehicle() == null) {
|
|
|
|
base.getEntity().addPassenger(hologram.getEntity());
|
|
|
|
}
|
2024-03-16 16:10:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-14 19:42:09 +02:00
|
|
|
private static final Pattern ITEM_MATCHER = Pattern.compile("<item:(.*?)([:].*?)?>");
|
|
|
|
private static boolean SUPPORTS_DISPLAY = false;
|
|
|
|
static {
|
|
|
|
try {
|
|
|
|
SUPPORTS_DISPLAY = Class.forName("org.bukkit.entity.Display") != null;
|
|
|
|
} catch (Throwable e) {
|
|
|
|
}
|
|
|
|
}
|
2020-06-30 12:17:14 +02:00
|
|
|
}
|