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.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.bukkit.ChatColor;
import org.bukkit.Location;
@ -13,7 +14,6 @@ import org.bukkit.entity.EntityType;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.inventory.ItemStack;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
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.util.Colorizer;
import net.citizensnpcs.api.util.DataKey;
import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.api.util.Placeholders;
import net.citizensnpcs.api.util.SpigotUtil;
import net.citizensnpcs.util.NMS;
import net.citizensnpcs.util.Util;
/**
* Persists a hologram attached to the NPC.
@ -43,7 +43,6 @@ public class HologramTrait extends Trait {
private boolean lastNameplateVisible;
@Persist
private double lineHeight = -1;
private final List<NPC> lineHolograms = Lists.newArrayList();
private final List<HologramLine> lines = Lists.newArrayList();
private NPC nameNPC;
private final NPCRegistry registry = CitizensAPI.createCitizensBackedNPCRegistry(new MemoryNPCDataStore());
@ -74,14 +73,17 @@ public class HologramTrait extends Trait {
*/
public void addTemporaryLine(String text, int ticks) {
lines.add(new HologramLine(text, false, ticks));
reloadLineHolograms();
}
/**
* Clears all hologram lines
*/
public void clear() {
for (HologramLine line : lines) {
line.removeNPC();
}
lines.clear();
reloadLineHolograms();
}
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.matchMaterial(itemMatcher.group(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);
((ArmorStand) hologramNPC.getEntity()).addPassenger(itemNPC.getEntity());
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.
*/
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
public void load(DataKey root) {
lines.clear();
clear();
for (DataKey key : root.getRelative("lines").getIntegerSubKeys()) {
lines.add(new HologramLine(key.getString(""), true));
}
@ -172,10 +179,10 @@ public class HologramTrait extends Trait {
nameNPC.destroy();
nameNPC = null;
}
for (NPC npc : lineHolograms) {
npc.destroy();
for (HologramLine line : lines) {
line.removeNPC();
}
lineHolograms.clear();
}
@Override
@ -187,6 +194,7 @@ public class HologramTrait extends Trait {
public void onSpawn() {
if (!npc.isSpawned())
return;
lastNameplateVisible = Boolean
.parseBoolean(npc.data().<Object> get(NPC.Metadata.NAMEPLATE_VISIBLE, true).toString());
currentLoc = npc.getStoredLocation();
@ -195,21 +203,20 @@ public class HologramTrait extends Trait {
}
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() {
for (NPC npc : lineHolograms) {
npc.destroy();
for (HologramLine line : lines) {
line.removeNPC();
}
lineHolograms.clear();
if (!npc.isSpawned())
return;
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())
return;
lines.remove(idx);
lines.remove(idx).removeNPC();
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
|| Math.abs(lastEntityHeight - getEntityHeight()) >= 0.05;
lastNameplateVisible = nameplateVisible;
if (update) {
if (updatePosition) {
currentLoc = npc.getStoredLocation();
lastEntityHeight = getEntityHeight();
}
if (nameNPC != null && nameNPC.isSpawned()) {
if (update) {
if (updatePosition) {
nameNPC.teleport(currentLoc.clone().add(0, getEntityHeight(), 0), TeleportCause.PLUGIN);
}
nameNPC.setName(npc.getFullName());
}
for (int i = 0; i < lineHolograms.size(); i++) {
NPC hologramNPC = lineHolograms.get(i);
if (!hologramNPC.isSpawned())
for (int i = 0; i < lines.size(); i++) {
HologramLine line = lines.get(i);
NPC hologramNPC = line.hologram;
if (hologramNPC == null || !hologramNPC.isSpawned())
continue;
if (update) {
if (updatePosition) {
hologramNPC.teleport(currentLoc.clone().add(0, lastEntityHeight
+ (direction == HologramDirection.BOTTOM_UP ? getHeight(i) : getMaxHeight() - getHeight(i)), 0),
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) {
line.removeNPC();
lines.remove(i--);
continue;
}
String text = line.text;
if (ITEM_MATCHER.matcher(text).matches()) {
text = null;
@ -339,13 +343,11 @@ public class HologramTrait extends Trait {
return;
}
lines.get(idx).text = text;
if (idx < lineHolograms.size()) {
lineHolograms.get(idx).setName(Placeholders.replace(text, null, npc));
return;
HologramLine line = lines.get(idx);
line.setText(text);
if (line.hologram == null) {
reloadLineHolograms();
}
reloadLineHolograms();
}
/**
@ -367,9 +369,10 @@ public class HologramTrait extends Trait {
}
private class HologramLine {
NPC hologram;
boolean persist;
String text;
public int ticks;
int ticks;
public HologramLine(String text, boolean persist) {
this(text, persist, -1);
@ -380,7 +383,27 @@ public class HologramTrait extends Trait {
this.persist = persist;
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) {
Paginator paginator = new Paginator().header("Current Texts").enablePageSwitcher();
for (int i = 0; i < text.size(); i++) {
paginator.addLine("<yellow>" + text.get(i) + " <green>(<click:suggest_command:edit " + i
+ "><yellow>edit</click>) (<click:run_command:remove " + i
paginator.addLine(text.get(i) + " <green>(<click:suggest_command:edit " + i
+ " ><yellow>edit</click>) (<click:run_command:remove " + i
+ "><hover:show_text:Remove this text><red>-</hover></click>)");
}
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.realistic-looking-set=[[Realistic looking]] 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.text-list-header=Current text:
citizens.editors.waypoints.wander.editing-regions-stop=Exited the region editor.