Skip to content

Commit

Permalink
Improve /npc text editor, implement speech bubbles, allow armor stand…
Browse files Browse the repository at this point in the history
…s to pathfind using the A* finder, allow /npc bossbar to work on all entities
  • Loading branch information
fullwall committed Jul 19, 2021
1 parent 55cf0c4 commit d4c557d
Show file tree
Hide file tree
Showing 21 changed files with 279 additions and 65 deletions.
4 changes: 3 additions & 1 deletion main/src/main/java/net/citizensnpcs/Settings.java
Expand Up @@ -61,6 +61,7 @@ public enum Setting {
ALWAYS_USE_NAME_HOLOGRAM("npc.always-use-name-holograms", false),
ASTAR_ITERATIONS_PER_TICK("npc.pathfinding.new-finder.iterations-per-tick", 5000),
AUTH_SERVER_URL("general.authlib.profile-url", "https://sessionserver.mojang.com/session/minecraft/profile/"),
BOSSBAR_RANGE("npc.default.bossbar-view-range", 64),
CHAT_BYSTANDERS_HEAR_TARGETED_CHAT("npc.chat.options.bystanders-hear-targeted-chat", false),
CHAT_FORMAT("npc.chat.format.no-targets", "[<npc>]: <text>"),
CHAT_FORMAT_TO_BYSTANDERS("npc.chat.format.with-target-to-bystanders", "[<npc>] -> [<target>]: <text>"),
Expand All @@ -85,7 +86,7 @@ public enum Setting {
DEFAULT_NPC_LIMIT("npc.limits.default-limit", 10),
DEFAULT_PATH_DISTANCE_MARGIN("npc.pathfinding.default-path-distance-margin", 1),
DEFAULT_PATHFINDER_UPDATE_PATH_RATE("npc.pathfinding.update-path-rate", 20),
DEFAULT_PATHFINDING_RANGE("npc.default.pathfinding.range", 25F),
DEFAULT_PATHFINDING_RANGE("npc.default.pathfinding.range", 75F),
DEFAULT_RANDOM_LOOK_CLOSE("npc.default.look-close.random-look-enabled", false),
DEFAULT_RANDOM_LOOK_DELAY("npc.default.look-close.random-look-delay", 60),
DEFAULT_RANDOM_TALKER("npc.default.random-talker", false),
Expand All @@ -103,6 +104,7 @@ public void loadFromKey(DataKey root) {
value = list;
}
},
DEFAULT_TEXT_SPEECH_BUBBLE_TICKS("npc.text.speech-bubble-ticks", 50),
DISABLE_LOOKCLOSE_WHILE_NAVIGATING("npc.default.look-close.disable-while-navigating", true),
DISABLE_MC_NAVIGATION_FALLBACK("npc.pathfinding.disable-mc-fallback-navigation", true),
DISABLE_TABLIST("npc.tablist.disable", true),
Expand Down
Expand Up @@ -137,7 +137,7 @@ public boolean update() {
Location currLoc = npc.getEntity().getLocation(NPC_LOCATION);
Vector destVector = new Vector(vector.getX() + 0.5, vector.getY(), vector.getZ() + 0.5);
/* Proper door movement - gets stuck on corners at times
Block block = currLoc.getWorld().getBlockAt(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ());
if (MinecraftBlockExaminer.isDoor(block.getType())) {
Door door = (Door) block.getState().getData();
Expand Down Expand Up @@ -165,7 +165,7 @@ public boolean update() {
}
double distance = xzDistance + dY * dY;

if (npc.getEntity() instanceof LivingEntity) {
if (npc.getEntity() instanceof LivingEntity && !npc.getEntity().getType().name().contains("ARMOR_STAND")) {
NMS.setDestination(npc.getEntity(), destVector.getX(), destVector.getY(), destVector.getZ(),
params.speed());
} else {
Expand Down
Expand Up @@ -115,7 +115,7 @@ public double getLineHeight() {
* @return the hologram lines, in bottom-up order
*/
public List<String> getLines() {
return Lists.newArrayList(lines);
return lines;
}

private double getMaxHeight() {
Expand Down
Expand Up @@ -21,7 +21,7 @@ public Prompt acceptValidatedInput(ConversationContext context, Number input) {
Player player = (Player) context.getForWhom();
if (!text.sendPage(player, input.intValue())) {
Messaging.sendErrorTr(player, Messages.TEXT_EDITOR_INVALID_PAGE);
return new TextStartPrompt(text);
return new TextBasePrompt(text);
}
return (Prompt) context.getSessionData("previous");
}
Expand Down
103 changes: 76 additions & 27 deletions main/src/main/java/net/citizensnpcs/trait/text/Text.java
Expand Up @@ -31,6 +31,7 @@
import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.api.util.Paginator;
import net.citizensnpcs.editor.Editor;
import net.citizensnpcs.trait.HologramTrait;
import net.citizensnpcs.trait.Toggleable;
import net.citizensnpcs.util.Messages;
import net.citizensnpcs.util.Util;
Expand All @@ -40,6 +41,7 @@
*/
@TraitName("text")
public class Text extends Trait implements Runnable, Toggleable, Listener, ConversationAbandonedListener {
private int bubbleTicks;
private final Map<UUID, Long> cooldowns = Maps.newHashMap();
private int currentIndex;
private int delay = -1;
Expand All @@ -48,6 +50,8 @@ public class Text extends Trait implements Runnable, Toggleable, Listener, Conve
private boolean randomTalker = Setting.DEFAULT_RANDOM_TALKER.asBoolean();
private double range = Setting.DEFAULT_TALK_CLOSE_RANGE.asDouble();
private boolean realisticLooker = Setting.DEFAULT_REALISTIC_LOOKING.asBoolean();
private boolean speechBubbles;
private int speechIndex = -1;
private boolean talkClose = Setting.DEFAULT_TALK_CLOSE.asBoolean();
private final List<String> text = new ArrayList<String>();

Expand All @@ -66,6 +70,14 @@ public void add(String string) {
text.add(string);
}

private void clearBubble() {
if (speechIndex < npc.getOrAddTrait(HologramTrait.class).getLines().size()) {
npc.getOrAddTrait(HologramTrait.class).removeLine(speechIndex);
speechIndex = -1;
}
bubbleTicks = 0;
}

@Override
public void conversationAbandoned(ConversationAbandonedEvent event) {
}
Expand All @@ -88,7 +100,7 @@ public void edit(int index, String newText) {
public Editor getEditor(final Player player) {
final Conversation conversation = new ConversationFactory(plugin).addConversationAbandonedListener(this)
.withLocalEcho(false).withEscapeSequence("/npc text").withEscapeSequence("exit").withModality(false)
.withFirstPrompt(new TextStartPrompt(this)).buildConversation(player);
.withFirstPrompt(new TextBasePrompt(this)).buildConversation(player);
return new Editor() {
@Override
public void begin() {
Expand All @@ -104,6 +116,21 @@ public void end() {
};
}

String getPageText(int page) {
Paginator paginator = new Paginator().header("Current Texts");
for (int i = 0; i < text.size(); i++)
paginator.addLine("<a>" + i + " <7>- <e>" + text.get(i));

return paginator.getPageText(page);
}

/**
* @return The list of all texts
*/
public List<String> getTexts() {
return text;
}

/**
* @return whether there is text at a certain index
*/
Expand All @@ -114,10 +141,6 @@ public boolean hasIndex(int index) {
@Override
public void load(DataKey key) throws NPCLoadException {
text.clear();
// TODO: legacy, remove later
for (DataKey sub : key.getIntegerSubKeys()) {
text.add(sub.getString(""));
}
for (DataKey sub : key.getRelative("text").getIntegerSubKeys()) {
text.add(sub.getString(""));
}
Expand Down Expand Up @@ -157,14 +180,21 @@ public void remove(int index) {

@Override
public void run() {
if (!talkClose || !npc.isSpawned())
if (!npc.isSpawned())
return;

if (bubbleTicks > 0 && --bubbleTicks == 0) {
clearBubble();
}

if (!talkClose)
return;
List<Entity> nearby = npc.getEntity().getNearbyEntities(range, range, range);
for (Entity search : nearby) {
if (!(search instanceof Player) || ((Player) search).getGameMode() == GameMode.SPECTATOR)
continue;
Player player = (Player) search;
// If the cooldown is not expired, do not send text

Long cooldown = cooldowns.get(player.getUniqueId());
if (cooldown != null) {
if (System.currentTimeMillis() < cooldown) {
Expand All @@ -173,7 +203,7 @@ public void run() {
cooldowns.remove(player.getUniqueId());
}
sendText(player);
// Add a cooldown if the text was successfully sent

int secondsDelta = delay != -1 ? delay
: RANDOM.nextInt(Setting.TALK_CLOSE_MAXIMUM_COOLDOWN.asInt())
+ Setting.TALK_CLOSE_MINIMUM_COOLDOWN.asInt();
Expand All @@ -192,16 +222,14 @@ public void save(DataKey key) {
key.setBoolean("realistic-looking", realisticLooker);
key.setDouble("range", range);
key.setString("talkitem", itemInHandPattern);
// TODO: legacy, remove later
for (int i = 0; i < 100; i++)
key.removeKey(String.valueOf(i));
key.removeKey("text");
for (int i = 0; i < text.size(); i++)
for (int i = 0; i < text.size(); i++) {
key.setString("text." + String.valueOf(i), text.get(i));
}
}

boolean sendPage(Player player, int page) {
Paginator paginator = new Paginator().header(npc.getName() + "'s Text Entries");
Paginator paginator = new Paginator().header("Current Texts");
for (int i = 0; i < text.size(); i++)
paginator.addLine("<a>" + i + " <7>- <e>" + text.get(i));

Expand All @@ -213,15 +241,27 @@ private boolean sendText(Player player) {
return false;

int index = 0;
if (randomTalker)
if (randomTalker) {
index = RANDOM.nextInt(text.size());
else {
if (currentIndex > text.size() - 1)
} else {
if (currentIndex > text.size() - 1) {
currentIndex = 0;
}
index = currentIndex++;
}

npc.getDefaultSpeechController().speak(new SpeechContext(text.get(index), player));
if (speechBubbles) {
HologramTrait trait = npc.getOrAddTrait(HologramTrait.class);
if (speechIndex == -1) {
speechIndex = trait.getLines().size();
trait.addLine(text.get(index));
bubbleTicks = Setting.DEFAULT_TEXT_SPEECH_BUBBLE_TICKS.asInt();
} else if (speechIndex < trait.getLines().size()) {
trait.setLine(speechIndex, text.get(index));
}
} else {
npc.getDefaultSpeechController().speak(new SpeechContext(text.get(index), player));
}
return true;
}

Expand All @@ -235,7 +275,13 @@ public void setDelay(int delay) {
this.delay = delay;
}

void setItemInHandPattern(String pattern) {
/**
* Sets the item in hand pattern required to talk to NPCs, if enabled.
*
* @param pattern
* The new pattern
*/
public void setItemInHandPattern(String pattern) {
itemInHandPattern = pattern;
}

Expand All @@ -248,7 +294,10 @@ public void setRange(double range) {
this.range = range;
}

boolean shouldTalkClose() {
/**
* @return Whether talking close is enabled.
*/
public boolean shouldTalkClose() {
return talkClose;
}

Expand All @@ -274,14 +323,14 @@ public boolean toggleRealisticLooking() {
return (realisticLooker = !realisticLooker);
}

@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Text{talk-close=" + talkClose + ",text=");
for (String line : text)
builder.append(line + ",");
builder.append("}");
return builder.toString();
/**
* Toggles using speech bubbles instead of messages.
*/
public boolean toggleSpeechBubbles() {
if (speechBubbles) {
clearBubble();
}
return (speechBubbles = !speechBubbles);
}

private static Random RANDOM = Util.getFastRandom();
Expand Down
93 changes: 93 additions & 0 deletions main/src/main/java/net/citizensnpcs/trait/text/TextBasePrompt.java
@@ -0,0 +1,93 @@
package net.citizensnpcs.trait.text;

import java.util.Arrays;

import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.conversations.ConversationContext;
import org.bukkit.conversations.Prompt;
import org.bukkit.conversations.StringPrompt;
import org.bukkit.entity.Player;

import com.google.common.base.Joiner;

import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.util.Messages;

public class TextBasePrompt extends StringPrompt {
private final Text text;

public TextBasePrompt(Text text) {
this.text = text;
}

@Override
public Prompt acceptInput(ConversationContext context, String original) {
String[] parts = ChatColor.stripColor(original.trim()).split(" ");
String input = parts[0];
CommandSender sender = (CommandSender) context.getForWhom();
if (input.equalsIgnoreCase("add")) {
text.add(Joiner.on(' ').join(Arrays.copyOfRange(parts, 1, parts.length)));
return this;
} else if (input.equalsIgnoreCase("edit")) {
return new TextEditStartPrompt(text);
} else if (input.equalsIgnoreCase("remove")) {
return new TextRemovePrompt(text);
} else if (input.equalsIgnoreCase("delay")) {
try {
int delay = Integer.parseInt(parts[1]);
text.setDelay(delay);
Messaging.sendTr(sender, Messages.TEXT_EDITOR_DELAY_SET, delay);
} catch (NumberFormatException e) {
Messaging.sendErrorTr(sender, Messages.TEXT_EDITOR_INVALID_DELAY);
} catch (ArrayIndexOutOfBoundsException e) {
Messaging.sendErrorTr(sender, Messages.TEXT_EDITOR_INVALID_DELAY);
}
} else if (input.equalsIgnoreCase("random")) {
Messaging.sendTr(sender, Messages.TEXT_EDITOR_RANDOM_TALKER_SET, text.toggleRandomTalker());
} else if (original.trim().equalsIgnoreCase("realistic looking")) {
Messaging.sendTr(sender, Messages.TEXT_EDITOR_REALISTIC_LOOKING_SET, text.toggleRealisticLooking());
} else if (original.trim().equalsIgnoreCase("speech bubbles")) {
Messaging.sendTr(sender, Messages.TEXT_EDITOR_SPEECH_BUBBLES_SET, text.toggleSpeechBubbles());
} else if (input.equalsIgnoreCase("close") || original.trim().equalsIgnoreCase("talk close")) {
Messaging.sendTr(sender, Messages.TEXT_EDITOR_CLOSE_TALKER_SET, text.toggle());
} else if (input.equalsIgnoreCase("range")) {
try {
double range = Math.min(Math.max(0, Double.parseDouble(parts[1])), Setting.MAX_TEXT_RANGE.asDouble());
text.setRange(range);
Messaging.sendTr(sender, Messages.TEXT_EDITOR_RANGE_SET, range);
} catch (NumberFormatException e) {
Messaging.sendErrorTr(sender, Messages.TEXT_EDITOR_INVALID_RANGE);
} catch (ArrayIndexOutOfBoundsException e) {
Messaging.sendErrorTr(sender, Messages.TEXT_EDITOR_INVALID_RANGE);
}
} else if (input.equalsIgnoreCase("item")) {
if (parts.length > 1) {
text.setItemInHandPattern(parts[1]);
Messaging.sendTr(sender, Messages.TEXT_EDITOR_SET_ITEM, parts[1]);
} else {
Messaging.sendErrorTr(sender, Messages.TEXT_EDITOR_MISSING_ITEM_PATTERN);
}
} else if (input.equalsIgnoreCase("help")) {
context.setSessionData("said-text", false);
Messaging.send(sender, getPromptText(context));
} else {
Messaging.sendErrorTr(sender, Messages.TEXT_EDITOR_INVALID_EDIT_TYPE);
}

return this;
}

@Override
public String getPromptText(ConversationContext context) {
if (Boolean.TRUE == context.getSessionData("said-text")) {
text.sendPage(((Player) context.getForWhom()), 1);
} else {
Messaging.send((Player) context.getForWhom(), Messaging.tr(Messages.TEXT_EDITOR_START_PROMPT));
text.sendPage(((Player) context.getForWhom()), 1);
context.setSessionData("said-text", Boolean.TRUE);
}
return "";
}
}
Expand Up @@ -21,7 +21,7 @@ public Prompt acceptInput(ConversationContext context, String input) {
int index = (Integer) context.getSessionData("index");
text.edit(index, input);
Messaging.sendTr((CommandSender) context.getForWhom(), Messages.TEXT_EDITOR_EDITED_TEXT, index, input);
return new TextStartPrompt(text);
return new TextBasePrompt(text);
}

@Override
Expand Down
Expand Up @@ -22,7 +22,7 @@ public Prompt acceptInput(ConversationContext context, String input) {
int index = Integer.parseInt(input);
if (!text.hasIndex(index)) {
Messaging.sendErrorTr(player, Messages.TEXT_EDITOR_INVALID_INDEX, index);
return new TextStartPrompt(text);
return new TextBasePrompt(text);
}
context.setSessionData("index", index);
return new TextEditPrompt(text);
Expand All @@ -33,7 +33,7 @@ public Prompt acceptInput(ConversationContext context, String input) {
}
}
Messaging.sendErrorTr(player, Messages.TEXT_EDITOR_INVALID_INPUT);
return new TextStartPrompt(text);
return new TextBasePrompt(text);
}

@Override
Expand Down

0 comments on commit d4c557d

Please sign in to comment.