Rework HologramTrait implementation

This commit is contained in:
fullwall 2022-10-16 10:41:34 +08:00
parent dee442e935
commit 2b19473832
3 changed files with 62 additions and 39 deletions

View File

@ -4,6 +4,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
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 org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.Location; import org.bukkit.Location;
@ -13,7 +14,6 @@ import org.bukkit.entity.EntityType;
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 com.google.common.collect.Collections2;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import net.citizensnpcs.Settings.Setting; import net.citizensnpcs.Settings.Setting;
@ -26,10 +26,10 @@ import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.trait.TraitName; import net.citizensnpcs.api.trait.TraitName;
import net.citizensnpcs.api.util.Colorizer; import net.citizensnpcs.api.util.Colorizer;
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;
import net.citizensnpcs.util.Util;
/** /**
* Persists a hologram attached to the NPC. * Persists a hologram attached to the NPC.
@ -43,7 +43,6 @@ public class HologramTrait extends Trait {
private boolean lastNameplateVisible; private boolean lastNameplateVisible;
@Persist @Persist
private double lineHeight = -1; private double lineHeight = -1;
private final List<NPC> lineHolograms = Lists.newArrayList();
private final List<HologramLine> lines = Lists.newArrayList(); private final List<HologramLine> lines = Lists.newArrayList();
private NPC nameNPC; private NPC nameNPC;
private final NPCRegistry registry = CitizensAPI.createCitizensBackedNPCRegistry(new MemoryNPCDataStore()); private final NPCRegistry registry = CitizensAPI.createCitizensBackedNPCRegistry(new MemoryNPCDataStore());
@ -74,14 +73,17 @@ public class HologramTrait extends Trait {
*/ */
public void addTemporaryLine(String text, int ticks) { public void addTemporaryLine(String text, int ticks) {
lines.add(new HologramLine(text, false, ticks)); lines.add(new HologramLine(text, false, ticks));
reloadLineHolograms();
} }
/** /**
* Clears all hologram lines * Clears all hologram lines
*/ */
public void clear() { public void clear() {
for (HologramLine line : lines) {
line.removeNPC();
}
lines.clear(); lines.clear();
reloadLineHolograms();
} }
private NPC createHologram(String line, double heightOffset) { private NPC createHologram(String line, double heightOffset) {
@ -96,6 +98,10 @@ public class HologramTrait extends Trait {
Material item = SpigotUtil.isUsing1_13API() ? Material.matchMaterial(itemMatcher.group(1), false) Material item = SpigotUtil.isUsing1_13API() ? Material.matchMaterial(itemMatcher.group(1), false)
: Material.matchMaterial(itemMatcher.group(1)); : Material.matchMaterial(itemMatcher.group(1));
NPC itemNPC = registry.createNPCUsingItem(EntityType.DROPPED_ITEM, "", new ItemStack(item, 1)); NPC itemNPC = registry.createNPCUsingItem(EntityType.DROPPED_ITEM, "", new ItemStack(item, 1));
if (itemMatcher.groupCount() > 1) {
itemNPC.getOrAddTrait(ScoreboardTrait.class)
.setColor(Util.matchEnum(ChatColor.values(), itemMatcher.group(2)));
}
itemNPC.spawn(currentLoc); itemNPC.spawn(currentLoc);
((ArmorStand) hologramNPC.getEntity()).addPassenger(itemNPC.getEntity()); ((ArmorStand) hologramNPC.getEntity()).addPassenger(itemNPC.getEntity());
itemNPC.addRunnable(new Runnable() { itemNPC.addRunnable(new Runnable() {
@ -130,7 +136,8 @@ public class HologramTrait extends Trait {
* Note: this is implementation-specific and may be removed at a later date. * Note: this is implementation-specific and may be removed at a later date.
*/ */
public Collection<ArmorStand> getHologramEntities() { public Collection<ArmorStand> getHologramEntities() {
return Collections2.transform(lineHolograms, (n) -> (ArmorStand) n.getEntity()); return lines.stream().filter(l -> l.hologram != null && l.hologram.getEntity() != null)
.map(l -> (ArmorStand) l.hologram.getEntity()).collect(Collectors.toList());
} }
/** /**
@ -160,7 +167,7 @@ public class HologramTrait extends Trait {
@Override @Override
public void load(DataKey root) { public void load(DataKey root) {
lines.clear(); clear();
for (DataKey key : root.getRelative("lines").getIntegerSubKeys()) { for (DataKey key : root.getRelative("lines").getIntegerSubKeys()) {
lines.add(new HologramLine(key.getString(""), true)); lines.add(new HologramLine(key.getString(""), true));
} }
@ -172,10 +179,10 @@ public class HologramTrait extends Trait {
nameNPC.destroy(); nameNPC.destroy();
nameNPC = null; nameNPC = null;
} }
for (NPC npc : lineHolograms) {
npc.destroy(); for (HologramLine line : lines) {
line.removeNPC();
} }
lineHolograms.clear();
} }
@Override @Override
@ -187,6 +194,7 @@ public class HologramTrait extends Trait {
public void onSpawn() { public void onSpawn() {
if (!npc.isSpawned()) if (!npc.isSpawned())
return; return;
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(); currentLoc = npc.getStoredLocation();
@ -195,21 +203,20 @@ public class HologramTrait extends Trait {
} }
for (int i = 0; i < lines.size(); i++) { for (int i = 0; i < lines.size(); i++) {
lineHolograms.add(createHologram(Placeholders.replace(lines.get(i).text, null, npc), getHeight(i))); lines.get(i).spawnNPC(getHeight(i));
} }
} }
private void reloadLineHolograms() { private void reloadLineHolograms() {
for (NPC npc : lineHolograms) { for (HologramLine line : lines) {
npc.destroy(); line.removeNPC();
} }
lineHolograms.clear();
if (!npc.isSpawned()) if (!npc.isSpawned())
return; return;
for (int i = 0; i < lines.size(); i++) { for (int i = 0; i < lines.size(); i++) {
lineHolograms.add(createHologram(Placeholders.replace(lines.get(i).text, null, npc), getHeight(i))); lines.get(i).spawnNPC(getHeight(i));
} }
} }
@ -222,7 +229,7 @@ public class HologramTrait extends Trait {
if (idx < 0 || idx >= lines.size()) if (idx < 0 || idx >= lines.size())
return; return;
lines.remove(idx); lines.remove(idx).removeNPC();
reloadLineHolograms(); reloadLineHolograms();
} }
@ -249,44 +256,41 @@ public class HologramTrait extends Trait {
} }
} }
boolean update = currentLoc.getWorld() != npc.getStoredLocation().getWorld() boolean updatePosition = currentLoc.getWorld() != npc.getStoredLocation().getWorld()
|| currentLoc.distance(npc.getStoredLocation()) >= 0.001 || lastNameplateVisible != nameplateVisible || currentLoc.distance(npc.getStoredLocation()) >= 0.001 || lastNameplateVisible != nameplateVisible
|| Math.abs(lastEntityHeight - getEntityHeight()) >= 0.05; || Math.abs(lastEntityHeight - getEntityHeight()) >= 0.05;
lastNameplateVisible = nameplateVisible; lastNameplateVisible = nameplateVisible;
if (update) { if (updatePosition) {
currentLoc = npc.getStoredLocation(); currentLoc = npc.getStoredLocation();
lastEntityHeight = getEntityHeight(); lastEntityHeight = getEntityHeight();
} }
if (nameNPC != null && nameNPC.isSpawned()) { if (nameNPC != null && nameNPC.isSpawned()) {
if (update) { if (updatePosition) {
nameNPC.teleport(currentLoc.clone().add(0, getEntityHeight(), 0), TeleportCause.PLUGIN); nameNPC.teleport(currentLoc.clone().add(0, getEntityHeight(), 0), TeleportCause.PLUGIN);
} }
nameNPC.setName(npc.getFullName()); nameNPC.setName(npc.getFullName());
} }
for (int i = 0; i < lineHolograms.size(); i++) { for (int i = 0; i < lines.size(); i++) {
NPC hologramNPC = lineHolograms.get(i); HologramLine line = lines.get(i);
if (!hologramNPC.isSpawned()) NPC hologramNPC = line.hologram;
if (hologramNPC == null || !hologramNPC.isSpawned())
continue; continue;
if (update) { if (updatePosition) {
hologramNPC.teleport(currentLoc.clone().add(0, lastEntityHeight hologramNPC.teleport(currentLoc.clone().add(0, lastEntityHeight
+ (direction == HologramDirection.BOTTOM_UP ? getHeight(i) : getMaxHeight() - getHeight(i)), 0), + (direction == HologramDirection.BOTTOM_UP ? getHeight(i) : getMaxHeight() - getHeight(i)), 0),
TeleportCause.PLUGIN); TeleportCause.PLUGIN);
} }
if (i >= lines.size()) {
Messaging.severe("More hologram NPCs than lines for ID", npc.getId(), "lines", lines);
break;
}
HologramLine line = lines.get(i);
if (line.ticks > 0 && --line.ticks == 0) { if (line.ticks > 0 && --line.ticks == 0) {
line.removeNPC();
lines.remove(i--); lines.remove(i--);
continue; continue;
} }
String text = line.text; String text = line.text;
if (ITEM_MATCHER.matcher(text).matches()) { if (ITEM_MATCHER.matcher(text).matches()) {
text = null; text = null;
@ -339,13 +343,11 @@ public class HologramTrait extends Trait {
return; return;
} }
lines.get(idx).text = text; HologramLine line = lines.get(idx);
if (idx < lineHolograms.size()) { line.setText(text);
lineHolograms.get(idx).setName(Placeholders.replace(text, null, npc)); if (line.hologram == null) {
return; reloadLineHolograms();
} }
reloadLineHolograms();
} }
/** /**
@ -367,9 +369,10 @@ public class HologramTrait extends Trait {
} }
private class HologramLine { private class HologramLine {
NPC hologram;
boolean persist; boolean persist;
String text; String text;
public int ticks; int ticks;
public HologramLine(String text, boolean persist) { public HologramLine(String text, boolean persist) {
this(text, persist, -1); this(text, persist, -1);
@ -380,7 +383,27 @@ public class HologramTrait extends Trait {
this.persist = persist; this.persist = persist;
this.ticks = ticks; this.ticks = ticks;
} }
public void removeNPC() {
if (hologram == null)
return;
hologram.destroy();
hologram = null;
}
public void setText(String text) {
this.text = text;
if (hologram != null) {
hologram.setName(Placeholders.replace(text, null, npc));
}
}
public void spawnNPC(double height) {
this.hologram = createHologram(Placeholders.replace(text, null, npc), height);
}
} }
private static final Pattern ITEM_MATCHER = Pattern.compile("<item:(.*?)>"); private static final Pattern ITEM_MATCHER = Pattern.compile("<item:(.*?)>|<item:(.*?):(.*?)>");
} }

View File

@ -233,8 +233,8 @@ public class Text extends Trait implements Runnable, Listener, ConversationAband
boolean sendPage(CommandSender player, int page) { boolean sendPage(CommandSender player, int page) {
Paginator paginator = new Paginator().header("Current Texts").enablePageSwitcher(); Paginator paginator = new Paginator().header("Current Texts").enablePageSwitcher();
for (int i = 0; i < text.size(); i++) { for (int i = 0; i < text.size(); i++) {
paginator.addLine("<yellow>" + text.get(i) + " <green>(<click:suggest_command:edit " + i paginator.addLine(text.get(i) + " <green>(<click:suggest_command:edit " + i
+ "><yellow>edit</click>) (<click:run_command:remove " + i + " ><yellow>edit</click>) (<click:run_command:remove " + i
+ "><hover:show_text:Remove this text><red>-</hover></click>)"); + "><hover:show_text:Remove this text><red>-</hover></click>)");
} }
return paginator.sendPage(player, page); return paginator.sendPage(player, page);

View File

@ -340,7 +340,7 @@ citizens.editors.text.range-set=[[Range]] set to [[{0}]].
citizens.editors.text.delay-set=[[Delay]] set to [[{0}]] seconds. citizens.editors.text.delay-set=[[Delay]] set to [[{0}]] seconds.
citizens.editors.text.realistic-looking-set=[[Realistic looking]] set to [[{0}]]. citizens.editors.text.realistic-looking-set=[[Realistic looking]] set to [[{0}]].
citizens.editors.text.speech-bubbles-set=[[Speech bubbles]] set to [[{0}]]. citizens.editors.text.speech-bubbles-set=[[Speech bubbles]] set to [[{0}]].
citizens.editors.text.start-prompt=<click:suggest_command:add ><yellow>Add text</click> | <click:suggest_command:item ><hover:show_text:'Set the talk item in hand pattern (set to <yellow>default</yellow> to clear)'><yellow>item</hover></click> | <click:suggest_command:range ><hover:show_text:Set the talking range in blocks><yellow>range</hover></click> | <click:suggest_command:delay ><hover:show_text:Set the talking delay in seconds><yellow>delay</hover></click><br><click:run_command:/npc text close><hover:show_text:Toggle sending messages when players get close>{0}talk close</hover></click> | <click:run_command:/npc text random><hover:show_text:Toggle random talking>{1}random</hover></click> | <click:run_command:/npc text speech bubbles><hover:show_text:Toggle showing text as holograms instead of chat messages>{2}speech bubbles</hover></click> | <click:run_command:/npc text realistic looking><hover:show_text:Toggle requiring line of sight before speaking>{3}realistic</hover></click> citizens.editors.text.start-prompt=<click:suggest_command:add ><yellow>Add text</click> | <click:suggest_command:item ><hover:show_text:"Set the talk item in hand pattern (set to <yellow>default</yellow> to clear)"><yellow>item</hover></click> | <click:suggest_command:range ><hover:show_text:Set the talking range in blocks><yellow>range</hover></click> | <click:suggest_command:delay ><hover:show_text:Set the talking delay in seconds><yellow>delay</hover></click><br><click:run_command:/npc text close><hover:show_text:Toggle sending messages when players get close>{0}talk close</hover></click> | <click:run_command:/npc text random><hover:show_text:Toggle random talking>{1}random</hover></click> | <click:run_command:/npc text speech bubbles><hover:show_text:Toggle showing text as holograms instead of chat messages>{2}speech bubbles</hover></click> | <click:run_command:/npc text realistic looking><hover:show_text:Toggle requiring line of sight before speaking>{3}realistic</hover></click>
citizens.editors.text.talk-item-set=[[Talk item pattern]] set to [[{0}]]. citizens.editors.text.talk-item-set=[[Talk item pattern]] set to [[{0}]].
citizens.editors.text.text-list-header=Current text: citizens.editors.text.text-list-header=Current text:
citizens.editors.waypoints.wander.editing-regions-stop=Exited the region editor. citizens.editors.waypoints.wander.editing-regions-stop=Exited the region editor.