mirror of
https://github.com/CitizensDev/Citizens2.git
synced 2024-11-22 10:36:10 +01:00
Rework hologram rendering (WIP: text displays not yet functional)
This commit is contained in:
parent
5f13340dea
commit
cd7f3366ab
@ -521,7 +521,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
|
|||||||
}
|
}
|
||||||
Translator.setInstance(new File(getDataFolder(), "lang"), locale);
|
Translator.setInstance(new File(getDataFolder(), "lang"), locale);
|
||||||
if (!locale.getLanguage().equals("en")) {
|
if (!locale.getLanguage().equals("en")) {
|
||||||
Messaging.logTr(Messages.CONTRIBUTE_TO_TRANSLATION_PROMPT);
|
Messaging.logTr(Messages.CONTRIBUTE_TO_TRANSLATION_PROMPT, locale.getLanguage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ import net.citizensnpcs.trait.ClickRedirectTrait;
|
|||||||
import net.citizensnpcs.trait.CommandTrait;
|
import net.citizensnpcs.trait.CommandTrait;
|
||||||
import net.citizensnpcs.trait.Controllable;
|
import net.citizensnpcs.trait.Controllable;
|
||||||
import net.citizensnpcs.trait.CurrentLocation;
|
import net.citizensnpcs.trait.CurrentLocation;
|
||||||
import net.citizensnpcs.trait.HologramTrait;
|
import net.citizensnpcs.trait.HologramTrait.HologramRenderer;
|
||||||
import net.citizensnpcs.trait.ShopTrait;
|
import net.citizensnpcs.trait.ShopTrait;
|
||||||
import net.citizensnpcs.trait.versioned.SnowmanTrait;
|
import net.citizensnpcs.trait.versioned.SnowmanTrait;
|
||||||
import net.citizensnpcs.util.ChunkCoord;
|
import net.citizensnpcs.util.ChunkCoord;
|
||||||
@ -469,17 +469,12 @@ public class EventListen implements Listener {
|
|||||||
if (skinnable.getSkinTracker().getSkin() != null) {
|
if (skinnable.getSkinTracker().getSkin() != null) {
|
||||||
skinnable.getSkinTracker().getSkin().apply(skinnable);
|
skinnable.getSkinTracker().getSkin().apply(skinnable);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (npc.isSpawned() && npc.getEntity().getType() == EntityType.PLAYER) {
|
|
||||||
onNPCPlayerLinkToPlayer(event);
|
onNPCPlayerLinkToPlayer(event);
|
||||||
}
|
}
|
||||||
ClickRedirectTrait crt = npc.getTraitNullable(ClickRedirectTrait.class);
|
if (npc.data().has(NPC.Metadata.HOLOGRAM_RENDERER)) {
|
||||||
if (crt != null) {
|
HologramRenderer hr = npc.data().get(NPC.Metadata.HOLOGRAM_RENDERER);
|
||||||
HologramTrait ht = crt.getRedirectNPC().getTraitNullable(HologramTrait.class);
|
|
||||||
if (ht != null) {
|
|
||||||
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(),
|
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(),
|
||||||
() -> ht.onHologramSeenByPlayer(npc, event.getPlayer()), 2);
|
() -> hr.onSeenByPlayer(event.getPlayer()), 2);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,11 +6,9 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.entity.EntityType;
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.entity.EntityDeathEvent;
|
import org.bukkit.event.entity.EntityDeathEvent;
|
||||||
@ -44,8 +42,7 @@ import net.citizensnpcs.api.npc.NPC;
|
|||||||
import net.citizensnpcs.api.trait.trait.MobType;
|
import net.citizensnpcs.api.trait.trait.MobType;
|
||||||
import net.citizensnpcs.api.util.Messaging;
|
import net.citizensnpcs.api.util.Messaging;
|
||||||
import net.citizensnpcs.npc.ai.NPCHolder;
|
import net.citizensnpcs.npc.ai.NPCHolder;
|
||||||
import net.citizensnpcs.trait.ClickRedirectTrait;
|
import net.citizensnpcs.trait.HologramTrait.HologramRenderer;
|
||||||
import net.citizensnpcs.trait.HologramTrait;
|
|
||||||
import net.citizensnpcs.trait.MirrorTrait;
|
import net.citizensnpcs.trait.MirrorTrait;
|
||||||
import net.citizensnpcs.trait.RotationTrait;
|
import net.citizensnpcs.trait.RotationTrait;
|
||||||
import net.citizensnpcs.trait.RotationTrait.PacketRotationSession;
|
import net.citizensnpcs.trait.RotationTrait.PacketRotationSession;
|
||||||
@ -71,16 +68,13 @@ public class ProtocolLibListener implements Listener {
|
|||||||
|
|
||||||
PacketContainer packet = event.getPacket();
|
PacketContainer packet = event.getPacket();
|
||||||
int version = manager.getProtocolVersion(event.getPlayer());
|
int version = manager.getProtocolVersion(event.getPlayer());
|
||||||
if (npc.data().has(NPC.Metadata.HOLOGRAM_FOR) || npc.data().has(NPC.Metadata.HOLOGRAM_LINE_SUPPLIER)) {
|
if (npc.data().has(NPC.Metadata.HOLOGRAM_RENDERER)) {
|
||||||
Function<Player, String> hvs = npc.data().get(NPC.Metadata.HOLOGRAM_LINE_SUPPLIER);
|
HologramRenderer hr = npc.data().get(NPC.Metadata.HOLOGRAM_RENDERER);
|
||||||
Object fakeName = null;
|
Object fakeName = null;
|
||||||
if (hvs != null) {
|
String suppliedName = hr.getPerPlayerText(npc, event.getPlayer());
|
||||||
String suppliedName = hvs.apply(event.getPlayer());
|
|
||||||
fakeName = version <= 340 ? suppliedName
|
fakeName = version <= 340 ? suppliedName
|
||||||
: Optional.of(Messaging.minecraftComponentFromRawMessage(suppliedName));
|
: Optional.of(Messaging.minecraftComponentFromRawMessage(suppliedName));
|
||||||
}
|
boolean sneaking = hr.isSneaking(npc, event.getPlayer());
|
||||||
boolean sneaking = npc.getOrAddTrait(ClickRedirectTrait.class).getRedirectNPC()
|
|
||||||
.getOrAddTrait(HologramTrait.class).isHologramSneaking(npc, event.getPlayer());
|
|
||||||
boolean delta = false;
|
boolean delta = false;
|
||||||
|
|
||||||
if (version < 761) {
|
if (version < 761) {
|
||||||
|
@ -108,6 +108,9 @@ public class Settings {
|
|||||||
DEFAULT_DISTANCE_MARGIN(
|
DEFAULT_DISTANCE_MARGIN(
|
||||||
"The default MOVEMENT distance in blocks where the NPC will move to before considering a path finished<br>Note: this is different from the PATHFINDING distance which is specified by path-distance-margin",
|
"The default MOVEMENT distance in blocks where the NPC will move to before considering a path finished<br>Note: this is different from the PATHFINDING distance which is specified by path-distance-margin",
|
||||||
"npc.pathfinding.default-distance-margin", 1),
|
"npc.pathfinding.default-distance-margin", 1),
|
||||||
|
DEFAULT_HOLOGRAM_RENDERER(
|
||||||
|
"The default renderer for holograms, must be one of the following:<br>interaction - matches inbuilt nametags most closely<br>display - allows for different colored backgrounds<br>armorstand - creates an armorstand and teleports it to the player",
|
||||||
|
"npc.hologram.default-renderer", "interaction"),
|
||||||
DEFAULT_LOOK_CLOSE("Enable look close by default", "npc.default.look-close.enabled", false),
|
DEFAULT_LOOK_CLOSE("Enable look close by default", "npc.default.look-close.enabled", false),
|
||||||
DEFAULT_LOOK_CLOSE_RANGE("Default look close range in blocks", "npc.default.look-close.range", 10),
|
DEFAULT_LOOK_CLOSE_RANGE("Default look close range in blocks", "npc.default.look-close.range", 10),
|
||||||
DEFAULT_NPC_HOLOGRAM_LINE_HEIGHT("Default distance between hologram lines", "npc.hologram.default-line-height",
|
DEFAULT_NPC_HOLOGRAM_LINE_HEIGHT("Default distance between hologram lines", "npc.hologram.default-line-height",
|
||||||
@ -166,9 +169,6 @@ public class Settings {
|
|||||||
"Minecraft will pick a 'close-enough' location when pathfinding to a block if it can't find a direct path<br>Disabled by default",
|
"Minecraft will pick a 'close-enough' location when pathfinding to a block if it can't find a direct path<br>Disabled by default",
|
||||||
"npc.pathfinding.disable-mc-fallback-navigation", true),
|
"npc.pathfinding.disable-mc-fallback-navigation", true),
|
||||||
DISABLE_TABLIST("Whether to remove NPCs from the tablist", "npc.tablist.disable", true),
|
DISABLE_TABLIST("Whether to remove NPCs from the tablist", "npc.tablist.disable", true),
|
||||||
DISPLAY_ENTITY_HOLOGRAMS(
|
|
||||||
"Whether to use display entities for holograms by default (in theory more performant than armor stands)<br>Requires 1.19.4 or above, defaults to false",
|
|
||||||
"npc.hologram.use-display-entities", false),
|
|
||||||
ENTITY_SPAWN_WAIT_DURATION(
|
ENTITY_SPAWN_WAIT_DURATION(
|
||||||
"Entities are no longer spawned until the chunks are loaded from disk<br>Wait for chunk loading for one second by default, increase if your disk is slow",
|
"Entities are no longer spawned until the chunks are loaded from disk<br>Wait for chunk loading for one second by default, increase if your disk is slow",
|
||||||
"general.entity-spawn-wait-ticks", "general.wait-for-entity-spawn", "1s"),
|
"general.entity-spawn-wait-ticks", "general.wait-for-entity-spawn", "1s"),
|
||||||
@ -284,11 +284,11 @@ public class Settings {
|
|||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Setting(String migrate, String path, Object value) {
|
Setting(String migrateOrComments, String path, Object value) {
|
||||||
if (migrate.contains(".")) {
|
if (migrateOrComments.contains(".") && !migrateOrComments.contains(" ")) {
|
||||||
this.migrate = migrate;
|
migrate = migrateOrComments;
|
||||||
} else {
|
} else {
|
||||||
comments = migrate;
|
comments = migrateOrComments;
|
||||||
}
|
}
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
@ -735,7 +735,7 @@ public class NPCCommands {
|
|||||||
throw new CommandException(Messaging.tr(Messages.NPC_CREATE_MISSING_MOBTYPE, args.getFlag("type")));
|
throw new CommandException(Messaging.tr(Messages.NPC_CREATE_MISSING_MOBTYPE, args.getFlag("type")));
|
||||||
}
|
}
|
||||||
int nameLength = SpigotUtil.getMaxNameLength(type);
|
int nameLength = SpigotUtil.getMaxNameLength(type);
|
||||||
if (Placeholders.replace(Messaging.parseComponents(name), sender, npc).length() > nameLength) {
|
if (Placeholders.replace(Messaging.stripColor(name), sender, npc).length() > nameLength) {
|
||||||
Messaging.sendErrorTr(sender, Messages.NPC_NAME_TOO_LONG, nameLength);
|
Messaging.sendErrorTr(sender, Messages.NPC_NAME_TOO_LONG, nameLength);
|
||||||
name = name.substring(0, nameLength);
|
name = name.substring(0, nameLength);
|
||||||
}
|
}
|
||||||
@ -1042,7 +1042,7 @@ public class NPCCommands {
|
|||||||
if (!(sender instanceof ConsoleCommandSender)
|
if (!(sender instanceof ConsoleCommandSender)
|
||||||
&& !followingNPC.getOrAddTrait(Owner.class).isOwnedBy(sender))
|
&& !followingNPC.getOrAddTrait(Owner.class).isOwnedBy(sender))
|
||||||
throw new CommandException(CommandMessages.MUST_BE_OWNER);
|
throw new CommandException(CommandMessages.MUST_BE_OWNER);
|
||||||
boolean following = !trait.isEnabled();
|
boolean following = explicit == null ? !trait.isEnabled() : explicit;
|
||||||
trait.follow(following ? followingNPC.getEntity() : null);
|
trait.follow(following ? followingNPC.getEntity() : null);
|
||||||
Messaging.sendTr(sender, following ? Messages.FOLLOW_SET : Messages.FOLLOW_UNSET, npc.getName(),
|
Messaging.sendTr(sender, following ? Messages.FOLLOW_SET : Messages.FOLLOW_UNSET, npc.getName(),
|
||||||
followingNPC.getName());
|
followingNPC.getName());
|
||||||
@ -2532,9 +2532,9 @@ public class NPCCommands {
|
|||||||
permission = "citizens.npc.rename")
|
permission = "citizens.npc.rename")
|
||||||
public void rename(CommandContext args, CommandSender sender, NPC npc) {
|
public void rename(CommandContext args, CommandSender sender, NPC npc) {
|
||||||
String oldName = npc.getName();
|
String oldName = npc.getName();
|
||||||
String newName = Messaging.parseComponents(args.getJoinedStrings(1));
|
String newName = args.getJoinedStrings(1);
|
||||||
int nameLength = SpigotUtil.getMaxNameLength(npc.getOrAddTrait(MobType.class).getType());
|
int nameLength = SpigotUtil.getMaxNameLength(npc.getOrAddTrait(MobType.class).getType());
|
||||||
if (newName.length() > nameLength) {
|
if (Placeholders.replace(Messaging.stripColor(newName), sender, npc).length() > nameLength) {
|
||||||
Messaging.sendErrorTr(sender, Messages.NPC_NAME_TOO_LONG, nameLength);
|
Messaging.sendErrorTr(sender, Messages.NPC_NAME_TOO_LONG, nameLength);
|
||||||
newName = newName.substring(0, nameLength);
|
newName = newName.substring(0, nameLength);
|
||||||
}
|
}
|
||||||
|
@ -192,7 +192,7 @@ public class CitizensNPC extends AbstractNPC {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean requiresNameHologram() {
|
public boolean requiresNameHologram() {
|
||||||
return !data().has(NPC.Metadata.HOLOGRAM_FOR)
|
return !data().has(NPC.Metadata.HOLOGRAM_RENDERER)
|
||||||
&& (super.requiresNameHologram() || Setting.ALWAYS_USE_NAME_HOLOGRAM.asBoolean());
|
&& (super.requiresNameHologram() || Setting.ALWAYS_USE_NAME_HOLOGRAM.asBoolean());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,8 +38,8 @@ public class FollowTrait extends Trait {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void cancelNavigationIfActive() {
|
private void cancelNavigationIfActive() {
|
||||||
if (npc.getNavigator().isNavigating() && this.entity != null && npc.getNavigator().getEntityTarget() != null
|
if (npc.getNavigator().isNavigating() && entity != null && npc.getNavigator().getEntityTarget() != null
|
||||||
&& this.entity == npc.getNavigator().getEntityTarget().getTarget()) {
|
&& entity == npc.getNavigator().getEntityTarget().getTarget()) {
|
||||||
npc.getNavigator().cancelNavigation();
|
npc.getNavigator().cancelNavigation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,10 +47,10 @@ public class FollowTrait extends Trait {
|
|||||||
/**
|
/**
|
||||||
* Sets the {@link Entity} to follow
|
* Sets the {@link Entity} to follow
|
||||||
*/
|
*/
|
||||||
public void follow(Entity entity) {
|
public void follow(Entity follow) {
|
||||||
followingUUID = entity == null ? null : entity.getUniqueId();
|
|
||||||
cancelNavigationIfActive();
|
cancelNavigationIfActive();
|
||||||
this.entity = null;
|
followingUUID = follow == null ? null : follow.getUniqueId();
|
||||||
|
entity = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entity getFollowing() {
|
public Entity getFollowing() {
|
||||||
|
@ -3,8 +3,7 @@ package net.citizensnpcs.trait;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.BiFunction;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -15,14 +14,17 @@ import org.bukkit.ChatColor;
|
|||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Display.Billboard;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.entity.EntityType;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.entity.TextDisplay;
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.util.Transformation;
|
||||||
import org.joml.Vector3d;
|
import org.joml.Vector3d;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import net.citizensnpcs.Settings.Setting;
|
import net.citizensnpcs.Settings.Setting;
|
||||||
@ -36,6 +38,7 @@ import net.citizensnpcs.api.persistence.Persist;
|
|||||||
import net.citizensnpcs.api.trait.Trait;
|
import net.citizensnpcs.api.trait.Trait;
|
||||||
import net.citizensnpcs.api.trait.TraitName;
|
import net.citizensnpcs.api.trait.TraitName;
|
||||||
import net.citizensnpcs.api.util.DataKey;
|
import net.citizensnpcs.api.util.DataKey;
|
||||||
|
import net.citizensnpcs.api.util.Messaging;
|
||||||
import net.citizensnpcs.api.util.Placeholders;
|
import net.citizensnpcs.api.util.Placeholders;
|
||||||
import net.citizensnpcs.api.util.SpigotUtil;
|
import net.citizensnpcs.api.util.SpigotUtil;
|
||||||
import net.citizensnpcs.util.NMS;
|
import net.citizensnpcs.util.NMS;
|
||||||
@ -45,11 +48,10 @@ import net.citizensnpcs.util.Util;
|
|||||||
* Manages a set of <em>holograms</em> attached to the NPC. Holograms are lines of text or items that follow the NPC at
|
* 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).
|
* some offset (typically vertically offset).
|
||||||
*/
|
*/
|
||||||
// TODO: refactor this class
|
// TODO: cleanup
|
||||||
@TraitName("hologramtrait")
|
@TraitName("hologramtrait")
|
||||||
public class HologramTrait extends Trait {
|
public class HologramTrait extends Trait {
|
||||||
private Location currentLoc;
|
private Location currentLoc;
|
||||||
private BiFunction<String, Player, String> customHologramSupplier;
|
|
||||||
private double lastEntityBbHeight = 0;
|
private double lastEntityBbHeight = 0;
|
||||||
private boolean lastNameplateVisible;
|
private boolean lastNameplateVisible;
|
||||||
@Persist
|
@Persist
|
||||||
@ -58,7 +60,6 @@ public class HologramTrait extends Trait {
|
|||||||
private HologramLine nameLine;
|
private HologramLine nameLine;
|
||||||
private final NPCRegistry registry = CitizensAPI.createCitizensBackedNPCRegistry(new MemoryNPCDataStore());
|
private final NPCRegistry registry = CitizensAPI.createCitizensBackedNPCRegistry(new MemoryNPCDataStore());
|
||||||
private int t;
|
private int t;
|
||||||
private boolean useDisplayEntities = Setting.DISPLAY_ENTITY_HOLOGRAMS.asBoolean();
|
|
||||||
@Persist
|
@Persist
|
||||||
private int viewRange = -1;
|
private int viewRange = -1;
|
||||||
|
|
||||||
@ -77,6 +78,11 @@ public class HologramTrait extends Trait {
|
|||||||
reloadLineHolograms();
|
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
|
* 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.
|
* the specified amount of ticks.
|
||||||
@ -101,64 +107,6 @@ public class HologramTrait extends Trait {
|
|||||||
lines.clear();
|
lines.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
private NPC createHologram(String line, double heightOffset) {
|
|
||||||
NPC hologramNPC = null;
|
|
||||||
if (useDisplayEntities) {
|
|
||||||
hologramNPC = registry.createNPC(EntityType.INTERACTION, line);
|
|
||||||
hologramNPC.addTrait(new ClickRedirectTrait(npc));
|
|
||||||
} else {
|
|
||||||
hologramNPC = registry.createNPC(EntityType.ARMOR_STAND, line);
|
|
||||||
hologramNPC.getOrAddTrait(ArmorStandTrait.class).setAsHelperEntityWithName(npc);
|
|
||||||
}
|
|
||||||
hologramNPC.data().set(NPC.Metadata.HOLOGRAM_FOR, npc.getUniqueId().toString());
|
|
||||||
if (Setting.PACKET_HOLOGRAMS.asBoolean()) {
|
|
||||||
hologramNPC.addTrait(PacketNPC.class);
|
|
||||||
}
|
|
||||||
if (viewRange != -1) {
|
|
||||||
hologramNPC.data().set(NPC.Metadata.TRACKING_RANGE, viewRange);
|
|
||||||
} else if (npc.data().has(NPC.Metadata.TRACKING_RANGE)) {
|
|
||||||
hologramNPC.data().set(NPC.Metadata.TRACKING_RANGE, npc.data().get(NPC.Metadata.TRACKING_RANGE));
|
|
||||||
}
|
|
||||||
hologramNPC.spawn(currentLoc.clone().add(0, getEntityBbHeight() + heightOffset, 0));
|
|
||||||
|
|
||||||
Matcher itemMatcher = ITEM_MATCHER.matcher(line);
|
|
||||||
if (itemMatcher.matches()) {
|
|
||||||
Material item = SpigotUtil.isUsing1_13API() ? Material.matchMaterial(itemMatcher.group(1), false)
|
|
||||||
: Material.matchMaterial(itemMatcher.group(1));
|
|
||||||
if (item == null) {
|
|
||||||
hologramNPC.destroy();
|
|
||||||
throw new IllegalStateException("Unknown material " + line);
|
|
||||||
}
|
|
||||||
ItemStack itemStack = new ItemStack(item, 1);
|
|
||||||
NPC itemNPC = registry.createNPCUsingItem(EntityType.DROPPED_ITEM, "", itemStack);
|
|
||||||
itemNPC.data().setPersistent(NPC.Metadata.NAMEPLATE_VISIBLE, false);
|
|
||||||
if (itemMatcher.group(2) != null) {
|
|
||||||
if (itemMatcher.group(2).charAt(1) == '{') {
|
|
||||||
Bukkit.getUnsafe().modifyItemStack(itemStack, itemMatcher.group(2).substring(1));
|
|
||||||
itemNPC.setItemProvider(() -> itemStack);
|
|
||||||
} else {
|
|
||||||
itemNPC.getOrAddTrait(ScoreboardTrait.class)
|
|
||||||
.setColor(Util.matchEnum(ChatColor.values(), itemMatcher.group(2).substring(1)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hologramNPC.data().has(NPC.Metadata.TRACKING_RANGE)) {
|
|
||||||
itemNPC.data().setPersistent(NPC.Metadata.TRACKING_RANGE,
|
|
||||||
hologramNPC.data().get(NPC.Metadata.TRACKING_RANGE));
|
|
||||||
}
|
|
||||||
itemNPC.getOrAddTrait(MountTrait.class).setMountedOn(hologramNPC.getUniqueId());
|
|
||||||
itemNPC.spawn(currentLoc);
|
|
||||||
NPC hn = hologramNPC;
|
|
||||||
itemNPC.addRunnable(() -> {
|
|
||||||
if (!itemNPC.isSpawned() || !hn.isSpawned()) {
|
|
||||||
itemNPC.destroy();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
lastEntityBbHeight = getEntityBbHeight();
|
|
||||||
return hologramNPC;
|
|
||||||
}
|
|
||||||
|
|
||||||
private double getEntityBbHeight() {
|
private double getEntityBbHeight() {
|
||||||
return NMS.getBoundingBoxHeight(npc.getEntity());
|
return NMS.getBoundingBoxHeight(npc.getEntity());
|
||||||
}
|
}
|
||||||
@ -175,14 +123,6 @@ public class HologramTrait extends Trait {
|
|||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Note: this is implementation-specific and may be removed at a later date.
|
|
||||||
*/
|
|
||||||
public Collection<Entity> getHologramEntities() {
|
|
||||||
return lines.stream().filter(l -> l.hologram != null && l.hologram.getEntity() != null)
|
|
||||||
.map(l -> l.hologram.getEntity()).collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The line height between each hologram line, in blocks
|
* @return The line height between each hologram line, in blocks
|
||||||
*/
|
*/
|
||||||
@ -197,24 +137,10 @@ public class HologramTrait extends Trait {
|
|||||||
return Lists.transform(lines, l -> l.text);
|
return Lists.transform(lines, l -> l.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public int getViewRange() {
|
||||||
* Note: this is implementation-specific and may be removed at a later date.
|
|
||||||
*/
|
|
||||||
public Entity getNameEntity() {
|
|
||||||
return nameLine != null && nameLine.hologram.isSpawned() ? nameLine.hologram.getEntity() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getViewRange() {
|
|
||||||
return viewRange;
|
return viewRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isHologramSneaking(NPC hologram, Player player) {
|
|
||||||
if (nameLine != null && hologram == nameLine.hologram && npc.getEntity() instanceof Player
|
|
||||||
&& ((Player) npc.getEntity()).isSneaking())
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void load(DataKey root) {
|
public void load(DataKey root) {
|
||||||
clear();
|
clear();
|
||||||
@ -229,33 +155,7 @@ public class HologramTrait extends Trait {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDespawn() {
|
public void onDespawn() {
|
||||||
if (nameLine != null) {
|
reloadLineHolograms();
|
||||||
nameLine.removeNPC();
|
|
||||||
nameLine = null;
|
|
||||||
}
|
|
||||||
for (HologramLine line : lines) {
|
|
||||||
line.removeNPC();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onHologramSeenByPlayer(NPC hologram, Player player) {
|
|
||||||
if (useDisplayEntities && npc.isSpawned()) {
|
|
||||||
double height = -1;
|
|
||||||
if (nameLine != null && hologram.equals(nameLine.hologram)) {
|
|
||||||
height = 0;
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < lines.size(); i++) {
|
|
||||||
if (hologram.equals(lines.get(i).hologram)) {
|
|
||||||
height = getHeight(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (height == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
NMS.linkTextInteraction(player, hologram.getEntity(), npc.getEntity(), height);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -270,32 +170,17 @@ public class HologramTrait extends Trait {
|
|||||||
|
|
||||||
lastNameplateVisible = Boolean
|
lastNameplateVisible = Boolean
|
||||||
.parseBoolean(npc.data().<Object> get(NPC.Metadata.NAMEPLATE_VISIBLE, true).toString());
|
.parseBoolean(npc.data().<Object> get(NPC.Metadata.NAMEPLATE_VISIBLE, true).toString());
|
||||||
currentLoc = npc.getStoredLocation();
|
|
||||||
if (npc.requiresNameHologram() && lastNameplateVisible) {
|
|
||||||
nameLine = new HologramLine(npc.getRawName(), false);
|
|
||||||
nameLine.spawnNPC(0);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < lines.size(); i++) {
|
|
||||||
lines.get(i).spawnNPC(getHeight(i));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reloadLineHolograms() {
|
private void reloadLineHolograms() {
|
||||||
for (HologramLine line : lines) {
|
for (HologramLine line : lines) {
|
||||||
line.removeNPC();
|
line.removeNPC();
|
||||||
}
|
}
|
||||||
if (!npc.isSpawned())
|
|
||||||
return;
|
|
||||||
if (npc.requiresNameHologram() && lastNameplateVisible) {
|
|
||||||
if (nameLine != null) {
|
if (nameLine != null) {
|
||||||
nameLine.removeNPC();
|
nameLine.removeNPC();
|
||||||
|
nameLine = null;
|
||||||
}
|
}
|
||||||
nameLine = new HologramLine(npc.getRawName(), false);
|
currentLoc = null;
|
||||||
nameLine.spawnNPC(0);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < lines.size(); i++) {
|
|
||||||
lines.get(i).spawnNPC(getHeight(i));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -318,9 +203,6 @@ public class HologramTrait extends Trait {
|
|||||||
onDespawn();
|
onDespawn();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (currentLoc == null) {
|
|
||||||
currentLoc = npc.getStoredLocation().clone();
|
|
||||||
}
|
|
||||||
boolean nameplateVisible = Boolean
|
boolean nameplateVisible = Boolean
|
||||||
.parseBoolean(npc.data().<Object> get(NPC.Metadata.NAMEPLATE_VISIBLE, true).toString());
|
.parseBoolean(npc.data().<Object> get(NPC.Metadata.NAMEPLATE_VISIBLE, true).toString());
|
||||||
if (npc.requiresNameHologram()) {
|
if (npc.requiresNameHologram()) {
|
||||||
@ -328,12 +210,13 @@ public class HologramTrait extends Trait {
|
|||||||
nameLine.removeNPC();
|
nameLine.removeNPC();
|
||||||
nameLine = null;
|
nameLine = null;
|
||||||
} else if (nameLine == null && nameplateVisible) {
|
} else if (nameLine == null && nameplateVisible) {
|
||||||
nameLine = new HologramLine(npc.getRawName(), false);
|
nameLine = new HologramLine(npc.getRawName(),
|
||||||
nameLine.spawnNPC(0);
|
SUPPORTS_DISPLAY ? new InteractionVehicleRenderer() : new ArmorstandVehicleRenderer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Location npcLoc = npc.getStoredLocation();
|
Location npcLoc = npc.getStoredLocation();
|
||||||
boolean updatePosition = Setting.HOLOGRAM_ALWAYS_UPDATE_POSITION.asBoolean()
|
Vector3d offset = new Vector3d();
|
||||||
|
boolean updatePosition = Setting.HOLOGRAM_ALWAYS_UPDATE_POSITION.asBoolean() || currentLoc == null
|
||||||
|| currentLoc.getWorld() != npcLoc.getWorld() || currentLoc.distance(npcLoc) >= 0.001
|
|| currentLoc.getWorld() != npcLoc.getWorld() || currentLoc.distance(npcLoc) >= 0.001
|
||||||
|| lastNameplateVisible != nameplateVisible
|
|| lastNameplateVisible != nameplateVisible
|
||||||
|| Math.abs(lastEntityBbHeight - getEntityBbHeight()) >= 0.05;
|
|| Math.abs(lastEntityBbHeight - getEntityBbHeight()) >= 0.05;
|
||||||
@ -349,45 +232,28 @@ public class HologramTrait extends Trait {
|
|||||||
currentLoc = npcLoc.clone();
|
currentLoc = npcLoc.clone();
|
||||||
lastEntityBbHeight = getEntityBbHeight();
|
lastEntityBbHeight = getEntityBbHeight();
|
||||||
}
|
}
|
||||||
if (nameLine != null && nameLine.hologram.isSpawned()) {
|
if (nameLine != null) {
|
||||||
if (updatePosition && !useDisplayEntities) {
|
if (updatePosition) {
|
||||||
nameLine.hologram.teleport(npcLoc.clone().add(0, getEntityBbHeight(), 0), TeleportCause.PLUGIN);
|
nameLine.render(offset);
|
||||||
}
|
}
|
||||||
if (updateName) {
|
if (updateName) {
|
||||||
nameLine.setText(npc.getRawName());
|
nameLine.setText(npc.getRawName());
|
||||||
}
|
}
|
||||||
if (useDisplayEntities && nameLine.hologram.getEntity().getVehicle() == null) {
|
|
||||||
npc.getEntity().addPassenger(nameLine.hologram.getEntity());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (int i = 0; i < lines.size(); i++) {
|
for (int i = 0; i < lines.size(); i++) {
|
||||||
HologramLine line = lines.get(i);
|
HologramLine line = lines.get(i);
|
||||||
NPC hologramNPC = line.hologram;
|
|
||||||
|
|
||||||
if (hologramNPC == null || !hologramNPC.isSpawned())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (line.ticks > 0 && --line.ticks == 0) {
|
if (line.ticks > 0 && --line.ticks == 0) {
|
||||||
lines.remove(i--).removeNPC();
|
lines.remove(i--).removeNPC();
|
||||||
;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (updatePosition && !useDisplayEntities) {
|
if (updatePosition) {
|
||||||
Location tp = npcLoc.clone().add(0, lastEntityBbHeight + getHeight(i), 0);
|
offset.y = getHeight(i);
|
||||||
hologramNPC.teleport(tp, TeleportCause.PLUGIN);
|
line.render(offset);
|
||||||
}
|
}
|
||||||
if (useDisplayEntities && hologramNPC.getEntity().getVehicle() == null) {
|
if (updateName) {
|
||||||
npc.getEntity().addPassenger(hologramNPC.getEntity());
|
line.setText(line.text);
|
||||||
}
|
}
|
||||||
String text = line.text;
|
|
||||||
if (ITEM_MATCHER.matcher(text).matches()) {
|
|
||||||
hologramNPC.data().set(NPC.Metadata.NAMEPLATE_VISIBLE, false);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!updateName) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
line.setText(text);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,10 +287,8 @@ public class HologramTrait extends Trait {
|
|||||||
}
|
}
|
||||||
HologramLine line = lines.get(idx);
|
HologramLine line = lines.get(idx);
|
||||||
line.setText(text);
|
line.setText(text);
|
||||||
if (line.hologram == null) {
|
|
||||||
reloadLineHolograms();
|
reloadLineHolograms();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the line height
|
* Sets the line height
|
||||||
@ -442,7 +306,7 @@ public class HologramTrait extends Trait {
|
|||||||
* Sets the margin of a line at a specific index
|
* Sets the margin of a line at a specific index
|
||||||
*
|
*
|
||||||
* @param idx
|
* @param idx
|
||||||
* The index
|
* The line index
|
||||||
* @param type
|
* @param type
|
||||||
* The margin type, top or bottom
|
* The margin type, top or bottom
|
||||||
* @param margin
|
* @param margin
|
||||||
@ -457,63 +321,43 @@ public class HologramTrait extends Trait {
|
|||||||
reloadLineHolograms();
|
reloadLineHolograms();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation-specific method: {@see NPC.Metadata#HOLOGRAM_LINE_SUPPLIER}
|
|
||||||
*/
|
|
||||||
public void setPerPlayerTextSupplier(BiFunction<String, Player, String> nameSupplier) {
|
|
||||||
customHologramSupplier = nameSupplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUseDisplayEntities(boolean use) {
|
|
||||||
useDisplayEntities = use;
|
|
||||||
reloadLineHolograms();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setViewRange(int range) {
|
public void setViewRange(int range) {
|
||||||
this.viewRange = range;
|
this.viewRange = range;
|
||||||
reloadLineHolograms();
|
reloadLineHolograms();
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class AbstractRenderer implements HologramRenderer {
|
public class ArmorstandRenderer extends SingleEntityHologramRenderer {
|
||||||
protected NPC npc;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destroy() {
|
protected NPC createNPC(Entity base, String name, Vector3d offset) {
|
||||||
if (npc != null) {
|
NPC npc = registry.createNPC(EntityType.ARMOR_STAND, name);
|
||||||
npc.destroy();
|
npc.getOrAddTrait(ArmorStandTrait.class).setAsHelperEntityWithName(npc);
|
||||||
npc = null;
|
return npc;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(Entity base, Vector3d offset) {
|
protected void render0(NPC npc, Vector3d offset) {
|
||||||
if (npc == null)
|
hologram.getEntity().teleport(npc.getStoredLocation().clone().add(offset.x, offset.y, offset.z),
|
||||||
return;
|
|
||||||
npc.getEntity().teleport(base.getLocation().clone().add(offset.x, offset.y, offset.z),
|
|
||||||
TeleportCause.PLUGIN);
|
TeleportCause.PLUGIN);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract NPC spawnNPC(Entity base, String text, Vector3d offset);
|
public class ArmorstandVehicleRenderer extends ArmorstandRenderer {
|
||||||
|
@Override
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateText(Entity base, String text) {
|
public void render0(NPC base, Vector3d offset) {
|
||||||
if (npc == null)
|
if (hologram.getEntity().getVehicle() == null) {
|
||||||
return;
|
base.getEntity().addPassenger(hologram.getEntity());
|
||||||
NMS.setCustomName(base, text, text);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ArmorstandRenderer extends AbstractRenderer {
|
private class HologramLine {
|
||||||
@Override
|
|
||||||
protected NPC spawnNPC(Entity base, String name, Vector3d offset) {
|
|
||||||
NPC hologramNPC = registry.createNPC(EntityType.ARMOR_STAND, name);
|
|
||||||
hologramNPC.getOrAddTrait(ArmorStandTrait.class).setAsHelperEntityWithName(npc);
|
|
||||||
return hologramNPC;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class HologramLine implements Function<Player, String> {
|
|
||||||
NPC hologram;
|
|
||||||
double mb, mt;
|
double mb, mt;
|
||||||
boolean persist;
|
boolean persist;
|
||||||
HologramRenderer renderer;
|
HologramRenderer renderer;
|
||||||
@ -521,55 +365,166 @@ public class HologramTrait extends Trait {
|
|||||||
int ticks;
|
int ticks;
|
||||||
|
|
||||||
public HologramLine(String text, boolean persist) {
|
public HologramLine(String text, boolean persist) {
|
||||||
this(text, persist, -1);
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
public HologramLine(String text, boolean persist, int ticks) {
|
public HologramLine(String text, boolean persist, int ticks) {
|
||||||
setText(text);
|
this(text, persist, ticks,
|
||||||
this.persist = persist;
|
SUPPORTS_DISPLAY && Setting.DEFAULT_HOLOGRAM_RENDERER.asString().equalsIgnoreCase("interaction")
|
||||||
this.ticks = ticks;
|
? 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) {
|
||||||
if (ITEM_MATCHER.matcher(text).matches()) {
|
if (ITEM_MATCHER.matcher(text).matches()) {
|
||||||
mb = 0.21;
|
mb = 0.21;
|
||||||
mt = 0.07;
|
mt = 0.07;
|
||||||
|
hr = new ItemRenderer();
|
||||||
}
|
}
|
||||||
|
this.persist = persist;
|
||||||
|
this.ticks = ticks;
|
||||||
|
this.renderer = hr;
|
||||||
|
setText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public HologramLine(String text, HologramRenderer renderer) {
|
||||||
public String apply(Player viewer) {
|
this(text, false, -1, renderer);
|
||||||
return Placeholders.replace(text, viewer, npc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeNPC() {
|
public void removeNPC() {
|
||||||
if (hologram == null)
|
renderer.destroy();
|
||||||
return;
|
}
|
||||||
|
|
||||||
hologram.destroy();
|
public void render(Vector3d vector3d) {
|
||||||
renderer = null;
|
renderer.render(npc, vector3d);
|
||||||
hologram = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setText(String text) {
|
public void setText(String text) {
|
||||||
this.text = text == null ? "" : text;
|
this.text = text == null ? "" : text;
|
||||||
|
if (ITEM_MATCHER.matcher(text).matches()) {
|
||||||
|
renderer.destroy();
|
||||||
|
mb = 0.21;
|
||||||
|
mt = 0.07;
|
||||||
|
renderer = new ItemRenderer();
|
||||||
|
}
|
||||||
|
renderer.updateText(npc, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (hologram != null) {
|
public static interface HologramRenderer {
|
||||||
// renderer.updateText(hologram, text);
|
void destroy();
|
||||||
String name = Placeholders.replace(text, null, npc);
|
|
||||||
hologram.setName(name);
|
String getPerPlayerText(NPC npc, Player viewer);
|
||||||
if (Placeholders.containsPlaceholders(text)) {
|
|
||||||
hologram.data().set(NPC.Metadata.HOLOGRAM_LINE_SUPPLIER, this);
|
default boolean isSneaking(NPC npc, Player player) {
|
||||||
|
return NMS.isSneaking(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void onSeenByPlayer(Player player) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void render(NPC npc, Vector3d offset);
|
||||||
|
|
||||||
|
void updateText(NPC npc, String text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InteractionVehicleRenderer extends SingleEntityHologramRenderer {
|
||||||
|
private Vector3d lastOffset;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NPC createNPC(Entity base, String name, Vector3d offset) {
|
||||||
|
lastOffset = new Vector3d(offset);
|
||||||
|
return registry.createNPC(EntityType.INTERACTION, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ItemRenderer extends SingleEntityHologramRenderer {
|
||||||
|
@Override
|
||||||
|
protected NPC createNPC(Entity base, String name, Vector3d offset) {
|
||||||
|
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);
|
||||||
|
NPC npc = registry.createNPCUsingItem(EntityType.DROPPED_ITEM, "", itemStack);
|
||||||
|
npc.data().setPersistent(NPC.Metadata.NAMEPLATE_VISIBLE, false);
|
||||||
|
if (itemMatcher.group(2) != null) {
|
||||||
|
if (itemMatcher.group(2).charAt(1) == '{') {
|
||||||
|
Bukkit.getUnsafe().modifyItemStack(itemStack, itemMatcher.group(2).substring(1));
|
||||||
|
npc.setItemProvider(() -> itemStack);
|
||||||
} else {
|
} else {
|
||||||
hologram.data().set(NPC.Metadata.NAMEPLATE_VISIBLE, ChatColor.stripColor(name).length() > 0);
|
npc.getOrAddTrait(ScoreboardTrait.class)
|
||||||
hologram.data().remove(NPC.Metadata.HOLOGRAM_LINE_SUPPLIER);
|
.setColor(Util.matchEnum(ChatColor.values(), itemMatcher.group(2).substring(1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void spawn(NPC npc, String text, Vector3d offset) {
|
public abstract class SingleEntityHologramRenderer implements HologramRenderer {
|
||||||
// hologram = renderer.spawn(npc, text, offset);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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)) {
|
if (!hologram.hasTrait(ClickRedirectTrait.class)) {
|
||||||
hologram.addTrait(new ClickRedirectTrait(npc));
|
hologram.addTrait(new ClickRedirectTrait(npc));
|
||||||
}
|
}
|
||||||
hologram.data().set(NPC.Metadata.HOLOGRAM_FOR, npc.getUniqueId().toString());
|
hologram.data().set(NPC.Metadata.HOLOGRAM_RENDERER, this);
|
||||||
if (Setting.PACKET_HOLOGRAMS.asBoolean()) {
|
if (Setting.PACKET_HOLOGRAMS.asBoolean()) {
|
||||||
hologram.addTrait(PacketNPC.class);
|
hologram.addTrait(PacketNPC.class);
|
||||||
}
|
}
|
||||||
@ -579,67 +534,18 @@ public class HologramTrait extends Trait {
|
|||||||
hologram.data().set(NPC.Metadata.TRACKING_RANGE, npc.data().get(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));
|
hologram.spawn(npc.getEntity().getLocation().add(offset.x, offset.y, offset.z));
|
||||||
if (customHologramSupplier != null) {
|
|
||||||
hologram.data().set(NPC.Metadata.HOLOGRAM_LINE_SUPPLIER,
|
|
||||||
(Function<Player, String>) p -> customHologramSupplier.apply(text, p));
|
|
||||||
} else if (Placeholders.containsPlaceholders(text)) {
|
|
||||||
hologram.data().set(NPC.Metadata.HOLOGRAM_LINE_SUPPLIER, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void spawnNPC(double height) {
|
|
||||||
String name = Placeholders.replace(text, null, npc);
|
|
||||||
hologram = createHologram(name, height);
|
|
||||||
if (customHologramSupplier != null) {
|
|
||||||
hologram.data().set(NPC.Metadata.HOLOGRAM_LINE_SUPPLIER,
|
|
||||||
(Function<Player, String>) p -> customHologramSupplier.apply(text, p));
|
|
||||||
} else if (Placeholders.containsPlaceholders(text)) {
|
|
||||||
hologram.data().set(NPC.Metadata.HOLOGRAM_LINE_SUPPLIER, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static interface HologramRenderer {
|
|
||||||
void destroy();
|
|
||||||
|
|
||||||
void render(Entity base, Vector3d offset);
|
|
||||||
|
|
||||||
void updateText(Entity base, String text);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class InteractionRenderer extends AbstractRenderer {
|
|
||||||
public void render(Entity base, String name, Vector3d offset) {
|
|
||||||
if (this.npc.getEntity().getVehicle() == null) {
|
|
||||||
HologramTrait.this.npc.getEntity().addPassenger(this.npc.getEntity());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected NPC spawnNPC(Entity base, String name, Vector3d offset) {
|
public void updateText(NPC npc, String text) {
|
||||||
return registry.createNPC(EntityType.INTERACTION, name);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ItemRenderer extends AbstractRenderer {
|
|
||||||
@Override
|
|
||||||
protected NPC spawnNPC(Entity base, String name, Vector3d offset) {
|
|
||||||
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);
|
|
||||||
NPC itemNPC = registry.createNPCUsingItem(EntityType.DROPPED_ITEM, "", itemStack);
|
|
||||||
itemNPC.data().setPersistent(NPC.Metadata.NAMEPLATE_VISIBLE, false);
|
|
||||||
if (itemMatcher.group(2) != null) {
|
|
||||||
if (itemMatcher.group(2).charAt(1) == '{') {
|
|
||||||
Bukkit.getUnsafe().modifyItemStack(itemStack, itemMatcher.group(2).substring(1));
|
|
||||||
itemNPC.setItemProvider(() -> itemStack);
|
|
||||||
} else {
|
|
||||||
itemNPC.getOrAddTrait(ScoreboardTrait.class)
|
|
||||||
.setColor(Util.matchEnum(ChatColor.values(), itemMatcher.group(2).substring(1)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return itemNPC;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TabCompletions implements CompletionsProvider {
|
public static class TabCompletions implements CompletionsProvider {
|
||||||
@ -653,21 +559,35 @@ public class HologramTrait extends Trait {
|
|||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<String> LINE_ARGS = ImmutableList.of("set", "remove", "margintop", "marginbottom");
|
private static Set<String> LINE_ARGS = ImmutableSet.of("set", "remove", "margintop", "marginbottom");
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TextDisplayRenderer extends AbstractRenderer {
|
public class TextDisplayVehicleRenderer extends SingleEntityHologramRenderer {
|
||||||
public void render(Entity base, String name, Vector3d offset) {
|
@Override
|
||||||
if (this.npc.getEntity().getVehicle() == null) {
|
protected NPC createNPC(Entity base, String name, Vector3d offset) {
|
||||||
HologramTrait.this.npc.getEntity().addPassenger(this.npc.getEntity());
|
NPC npc = registry.createNPC(EntityType.TEXT_DISPLAY, name);
|
||||||
}
|
return npc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected NPC spawnNPC(Entity base, String name, Vector3d offset) {
|
public void render0(NPC base, Vector3d offset) {
|
||||||
return registry.createNPC(EntityType.TEXT_DISPLAY, name);
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Pattern ITEM_MATCHER = Pattern.compile("<item:(.*?)([:].*?)?>");
|
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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import org.bukkit.conversations.ConversationFactory;
|
|||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
@ -20,11 +19,9 @@ import net.citizensnpcs.Settings.Setting;
|
|||||||
import net.citizensnpcs.api.CitizensAPI;
|
import net.citizensnpcs.api.CitizensAPI;
|
||||||
import net.citizensnpcs.api.ai.speech.SpeechContext;
|
import net.citizensnpcs.api.ai.speech.SpeechContext;
|
||||||
import net.citizensnpcs.api.event.NPCRightClickEvent;
|
import net.citizensnpcs.api.event.NPCRightClickEvent;
|
||||||
import net.citizensnpcs.api.exception.NPCLoadException;
|
|
||||||
import net.citizensnpcs.api.persistence.Persist;
|
import net.citizensnpcs.api.persistence.Persist;
|
||||||
import net.citizensnpcs.api.trait.Trait;
|
import net.citizensnpcs.api.trait.Trait;
|
||||||
import net.citizensnpcs.api.trait.TraitName;
|
import net.citizensnpcs.api.trait.TraitName;
|
||||||
import net.citizensnpcs.api.util.DataKey;
|
|
||||||
import net.citizensnpcs.api.util.Messaging;
|
import net.citizensnpcs.api.util.Messaging;
|
||||||
import net.citizensnpcs.api.util.Paginator;
|
import net.citizensnpcs.api.util.Paginator;
|
||||||
import net.citizensnpcs.api.util.Placeholders;
|
import net.citizensnpcs.api.util.Placeholders;
|
||||||
@ -44,9 +41,9 @@ public class Text extends Trait implements Runnable, Listener {
|
|||||||
private int delay = -1;
|
private int delay = -1;
|
||||||
@Persist(value = "talkitem")
|
@Persist(value = "talkitem")
|
||||||
private String itemInHandPattern = "default";
|
private String itemInHandPattern = "default";
|
||||||
private final Plugin plugin;
|
|
||||||
@Persist(value = "random-talker")
|
@Persist(value = "random-talker")
|
||||||
private boolean randomTalker = Setting.DEFAULT_RANDOM_TALKER.asBoolean();
|
private boolean randomTalker = Setting.DEFAULT_RANDOM_TALKER.asBoolean();
|
||||||
|
@Persist
|
||||||
private double range = Setting.DEFAULT_TALK_CLOSE_RANGE.asDouble();
|
private double range = Setting.DEFAULT_TALK_CLOSE_RANGE.asDouble();
|
||||||
@Persist(value = "realistic-looking")
|
@Persist(value = "realistic-looking")
|
||||||
private boolean realisticLooker = Setting.DEFAULT_REALISTIC_LOOKING.asBoolean();
|
private boolean realisticLooker = Setting.DEFAULT_REALISTIC_LOOKING.asBoolean();
|
||||||
@ -54,11 +51,11 @@ public class Text extends Trait implements Runnable, Listener {
|
|||||||
private boolean speechBubbles;
|
private boolean speechBubbles;
|
||||||
@Persist(value = "talk-close")
|
@Persist(value = "talk-close")
|
||||||
private boolean talkClose = Setting.DEFAULT_TALK_CLOSE.asBoolean();
|
private boolean talkClose = Setting.DEFAULT_TALK_CLOSE.asBoolean();
|
||||||
private final List<String> text = new ArrayList<>();
|
@Persist
|
||||||
|
private volatile List<String> text = new ArrayList<>();
|
||||||
|
|
||||||
public Text() {
|
public Text() {
|
||||||
super("text");
|
super("text");
|
||||||
plugin = CitizensAPI.getPlugin();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,9 +84,9 @@ public class Text extends Trait implements Runnable, Listener {
|
|||||||
* Builds a text editor in game for the supplied {@link Player}.
|
* Builds a text editor in game for the supplied {@link Player}.
|
||||||
*/
|
*/
|
||||||
public Editor getEditor(Player player) {
|
public Editor getEditor(Player player) {
|
||||||
Conversation conversation = new ConversationFactory(plugin).withLocalEcho(false).withEscapeSequence("/npc text")
|
Conversation conversation = new ConversationFactory(CitizensAPI.getPlugin()).withLocalEcho(false)
|
||||||
.withEscapeSequence("exit").withModality(false).withFirstPrompt(new TextBasePrompt(this))
|
.withEscapeSequence("/npc text").withEscapeSequence("exit").withModality(false)
|
||||||
.buildConversation(player);
|
.withFirstPrompt(new TextBasePrompt(this)).buildConversation(player);
|
||||||
return new Editor() {
|
return new Editor() {
|
||||||
@Override
|
@Override
|
||||||
public void begin() {
|
public void begin() {
|
||||||
@ -135,18 +132,6 @@ public class Text extends Trait implements Runnable, Listener {
|
|||||||
return randomTalker;
|
return randomTalker;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void load(DataKey key) throws NPCLoadException {
|
|
||||||
text.clear();
|
|
||||||
for (DataKey sub : key.getRelative("text").getIntegerSubKeys()) {
|
|
||||||
text.add(sub.getString(""));
|
|
||||||
}
|
|
||||||
if (text.isEmpty()) {
|
|
||||||
populateDefaultText();
|
|
||||||
}
|
|
||||||
range = key.getDouble("range");
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
private void onRightClick(NPCRightClickEvent event) {
|
private void onRightClick(NPCRightClickEvent event) {
|
||||||
if (!event.getNPC().equals(npc) || text.size() == 0)
|
if (!event.getNPC().equals(npc) || text.size() == 0)
|
||||||
@ -158,9 +143,12 @@ public class Text extends Trait implements Runnable, Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populateDefaultText() {
|
@Override
|
||||||
|
public void onSpawn() {
|
||||||
|
if (text.isEmpty()) {
|
||||||
text.addAll(Setting.DEFAULT_TEXT.asList());
|
text.addAll(Setting.DEFAULT_TEXT.asList());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove text at a given index.
|
* Remove text at a given index.
|
||||||
@ -179,15 +167,6 @@ public class Text extends Trait implements Runnable, Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save(DataKey key) {
|
|
||||||
key.setDouble("range", range);
|
|
||||||
key.removeKey("text");
|
|
||||||
for (int i = 0; i < text.size(); i++) {
|
|
||||||
key.setString("text." + String.valueOf(i), text.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean sendPage(CommandSender player, int page) {
|
boolean sendPage(CommandSender player, int page) {
|
||||||
Paginator paginator = new Paginator().header("Current Texts").enablePageSwitcher("/npc text page $page");
|
Paginator paginator = new Paginator().header("Current Texts").enablePageSwitcher("/npc text page $page");
|
||||||
for (int i = 0; i < text.size(); i++) {
|
for (int i = 0; i < text.size(); i++) {
|
||||||
|
@ -232,7 +232,7 @@ public class RabbitController extends MobEntityController {
|
|||||||
initPathfinder(); // make sure the evil goals include the default AI goals
|
initPathfinder(); // make sure the evil goals include the default AI goals
|
||||||
}
|
}
|
||||||
super.setRabbitType(type);
|
super.setRabbitType(type);
|
||||||
NMSImpl.clearGoals(npc, goalSelector, targetSelector);
|
NMSImpl.clearGoals(goalSelector, targetSelector);
|
||||||
} else if (NMSImpl.getRabbitTypeField() != null) {
|
} else if (NMSImpl.getRabbitTypeField() != null) {
|
||||||
datawatcher.set(NMSImpl.getRabbitTypeField(), type);
|
datawatcher.set(NMSImpl.getRabbitTypeField(), type);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user