Add custom tag parsing to shop, add /npc hologram insert, add /npc text send text to chat toggle

This commit is contained in:
fullwall 2024-09-08 19:41:53 +08:00
parent 81794f5ece
commit d0ff1d147e
8 changed files with 223 additions and 15 deletions

View File

@ -467,7 +467,6 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
shops.loadFromDisk();
shops.load();
getServer().getPluginManager().callEvent(new CitizensReloadEvent());
}

View File

@ -1286,7 +1286,7 @@ public class NPCCommands {
@Command(
aliases = { "npc" },
usage = "hologram add [text] | set [line #] [text] | remove [line #] | bgcolor [line #] (red,green,blue(,alpha)) | clear | lineheight [height] | viewrange [range] | margintop [line #] [margin] | marginbottom [line #] [margin]",
usage = "hologram add [text] | insert [line #] [text] | set [line #] [text] | remove [line #] | bgcolor [line #] (red,green,blue(,alpha)) | clear | lineheight [height] | viewrange [range] | margintop [line #] [margin] | marginbottom [line #] [margin]",
desc = "",
modifiers = { "hologram" },
min = 1,
@ -1295,8 +1295,8 @@ public class NPCCommands {
public void hologram(CommandContext args, CommandSender sender, NPC npc,
@Arg(
value = 1,
completions = { "add", "set", "bgcolor", "remove", "clear", "lineheight", "viewrange", "margintop",
"marginbottom" }) String action,
completions = { "add", "insert", "set", "bgcolor", "remove", "clear", "lineheight", "viewrange",
"margintop", "marginbottom" }) String action,
@Arg(value = 2, completionsProvider = HologramTrait.TabCompletions.class) String secondCompletion)
throws CommandException {
HologramTrait trait = npc.getOrAddTrait(HologramTrait.class);
@ -1314,7 +1314,8 @@ public class NPCCommands {
if (args.argsLength() == 2)
throw new CommandException(Messages.HOLOGRAM_INVALID_LINE);
int idx = Math.max(0, args.getInteger(2));
int idx = args.getString(2).equals("bottom") ? 0
: args.getString(2).equals("top") ? trait.getLines().size() - 1 : Math.max(0, args.getInteger(2));
if (idx >= trait.getLines().size())
throw new CommandException(Messages.HOLOGRAM_INVALID_LINE);
@ -1328,7 +1329,9 @@ public class NPCCommands {
trait.setDefaultBackgroundColor(Util.parseColor(args.getString(2)));
Messaging.sendTr(sender, Messages.HOLOGRAM_DEFAULT_BACKGROUND_COLOR_SET, args.getString(2));
} else {
int idx = Math.max(0, args.getInteger(2));
int idx = args.getString(2).equals("bottom") ? 0
: args.getString(2).equals("top") ? trait.getLines().size() - 1
: Math.max(0, args.getInteger(2));
if (idx >= trait.getLines().size())
throw new CommandException(Messages.HOLOGRAM_INVALID_LINE);
trait.setBackgroundColor(idx, Util.parseColor(args.getString(3)));
@ -1346,11 +1349,26 @@ public class NPCCommands {
trait.addLine(args.getJoinedStrings(2));
Messaging.sendTr(sender, Messages.HOLOGRAM_LINE_ADD, args.getJoinedStrings(2));
} else if (action.equalsIgnoreCase("insert")) {
if (args.argsLength() == 2)
throw new CommandException(Messages.HOLOGRAM_INVALID_LINE);
if (args.argsLength() == 3)
throw new CommandException(Messages.HOLOGRAM_TEXT_MISSING);
int idx = args.getString(2).equals("bottom") ? 0
: args.getString(2).equals("top") ? trait.getLines().size() - 1 : Math.max(0, args.getInteger(2));
if (idx > trait.getLines().size())
throw new CommandException(Messages.HOLOGRAM_INVALID_LINE);
trait.insertLine(idx, args.getJoinedStrings(3));
Messaging.sendTr(sender, Messages.HOLOGRAM_LINE_ADD, args.getJoinedStrings(3));
} else if (action.equalsIgnoreCase("remove")) {
if (args.argsLength() == 2)
throw new CommandException(Messages.HOLOGRAM_INVALID_LINE);
int idx = Math.max(0, args.getInteger(2));
int idx = args.getString(2).equals("bottom") ? 0
: args.getString(2).equals("top") ? trait.getLines().size() - 1 : Math.max(0, args.getInteger(2));
if (idx >= trait.getLines().size())
throw new CommandException(Messages.HOLOGRAM_INVALID_LINE);
@ -1369,7 +1387,8 @@ public class NPCCommands {
if (args.argsLength() == 2)
throw new CommandException(Messages.HOLOGRAM_INVALID_LINE);
int idx = Math.max(0, args.getInteger(2));
int idx = args.getString(2).equals("bottom") ? 0
: args.getString(2).equals("top") ? trait.getLines().size() - 1 : Math.max(0, args.getInteger(2));
if (idx >= trait.getLines().size())
throw new CommandException(Messages.HOLOGRAM_INVALID_LINE);
@ -1382,7 +1401,8 @@ public class NPCCommands {
if (args.argsLength() == 2)
throw new CommandException(Messages.HOLOGRAM_INVALID_LINE);
int idx = Math.max(0, args.getInteger(2));
int idx = args.getString(2).equals("bottom") ? 0
: args.getString(2).equals("top") ? trait.getLines().size() - 1 : Math.max(0, args.getInteger(2));
if (idx >= trait.getLines().size())
throw new CommandException(Messages.HOLOGRAM_INVALID_LINE);
@ -2725,7 +2745,7 @@ public class NPCCommands {
@Command(
aliases = { "npc" },
usage = "rotate (--towards [x,y,z]) (--body [yaw]) (--head [yaw]) (--pitch [pitch]) (-s(mooth))",
usage = "rotate (--towards [x,y,z]) (--toentity [name|uuid|me]) (--body [yaw]) (--head [yaw]) (--pitch [pitch]) (-s(mooth))",
desc = "",
flags = "s",
modifiers = { "rotate" },
@ -2733,7 +2753,8 @@ public class NPCCommands {
max = 1,
permission = "citizens.npc.rotate")
public void rotate(CommandContext args, CommandSender sender, NPC npc, @Flag("body") Float yaw,
@Flag("head") Float head, @Flag("pitch") Float pitch, @Flag("towards") Location towards) {
@Flag("head") Float head, @Flag("pitch") Float pitch, @Flag("towards") Location towards,
@Flag("toentity") String entity) throws CommandException {
if (args.hasFlag('s')) {
if (pitch == null) {
pitch = npc.getStoredLocation().getPitch();
@ -2748,6 +2769,18 @@ public class NPCCommands {
npc.getOrAddTrait(RotationTrait.class).getPhysicalSession().rotateToHave(yaw, pitch);
return;
}
if (entity != null) {
if (entity.equals("me")) {
towards = args.getSenderLocation();
} else {
try {
UUID uuid = UUID.fromString(entity);
towards = Bukkit.getPlayer(uuid).getLocation();
} catch (IllegalArgumentException ex) {
towards = Bukkit.getPlayerExact(entity).getLocation();
}
}
}
if (towards != null) {
npc.getOrAddTrait(RotationTrait.class).getPhysicalSession().rotateToFace(towards);
return;

View File

@ -220,6 +220,11 @@ public class HologramTrait extends Trait {
return viewRange;
}
public void insertLine(int idx, String text) {
lines.add(idx, new HologramLine(text, true, -1, createDefaultHologramRenderer()));
reloadLineHolograms();
}
@Override
public void load(DataKey root) {
clear();

View File

@ -49,6 +49,8 @@ public class Text extends Trait implements Runnable, Listener {
private double range = Setting.DEFAULT_TALK_CLOSE_RANGE.asDouble();
@Persist(value = "realistic-looking")
private boolean realisticLooker = Setting.DEFAULT_REALISTIC_LOOKING.asBoolean();
@Persist(value = "send-text-to-chat")
private boolean sendTextToChat = true;
@Persist(value = "speech-bubble-duration")
private int speechBubbleDuration = Setting.DEFAULT_TEXT_SPEECH_BUBBLE_DURATION.asTicks();
@Persist(value = "speech-bubbles")
@ -56,7 +58,7 @@ public class Text extends Trait implements Runnable, Listener {
@Persist(value = "talk-close")
private boolean talkClose = Setting.DEFAULT_TALK_CLOSE.asBoolean();
@Persist
private volatile List<String> text = new ArrayList<>();
private final List<String> text = new ArrayList<>();
public Text() {
super("text");
@ -211,10 +213,16 @@ public class Text extends Trait implements Runnable, Listener {
HologramTrait trait = npc.getOrAddTrait(HologramTrait.class);
trait.addTemporaryLine(Placeholders.replace(text.get(index), player, npc), speechBubbleDuration);
}
npc.getDefaultSpeechController().speak(new SpeechContext(text.get(index), player));
if (sendTextToChat) {
npc.getDefaultSpeechController().speak(new SpeechContext(text.get(index), player));
}
return true;
}
public boolean sendTextToChat() {
return sendTextToChat;
}
/**
* Set the text delay between messages.
*
@ -288,6 +296,13 @@ public class Text extends Trait implements Runnable, Listener {
return realisticLooker = !realisticLooker;
}
/**
* Toggles sending text through chat
*/
public boolean toggleSendTextToChat() {
return sendTextToChat = !sendTextToChat;
}
/**
* Toggles using speech bubbles instead of messages.
*/

View File

@ -74,6 +74,8 @@ public class TextBasePrompt extends StringPrompt {
}
} else if (input.equalsIgnoreCase("random")) {
text.toggleRandomTalker();
} else if (original.trim().equalsIgnoreCase("send text to chat")) {
text.toggleSendTextToChat();
} else if (original.trim().equalsIgnoreCase("realistic looking")) {
text.toggleRealisticLooking();
} else if (original.trim().equalsIgnoreCase("speech bubbles")) {
@ -121,7 +123,7 @@ public class TextBasePrompt extends StringPrompt {
Messaging.send((Player) context.getForWhom(),
Messaging.tr(Messages.TEXT_EDITOR_START_PROMPT, colorToggleableText(text.shouldTalkClose()),
colorToggleableText(text.isRandomTalker()), colorToggleableText(text.useSpeechBubbles()),
colorToggleableText(text.useRealisticLooking())));
colorToggleableText(text.useRealisticLooking()), colorToggleableText(text.sendTextToChat())));
int page = context.getSessionData("page") == null ? 1 : (int) context.getSessionData("page");
text.sendPage((Player) context.getForWhom(), page);
return "";

View File

@ -709,7 +709,7 @@
"citizens.editors.text.range-set" : "[[Range]] 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.start-prompt" : "<click:suggest_command:add ><yellow><u>Add text</u></click> | <click:suggest_command:item ><hover:show_text:\"Set the talk item in hand pattern (set to <yellow>default</yellow> to clear)\"><yellow><u>item</hover></click> | <click:suggest_command:range ><hover:show_text:Set the talking range in blocks><yellow><u>range</hover></click> | <click:suggest_command:delay ><hover:show_text:Set the talking delay in ticks><yellow><u>delay</u></yellow></hover></click><br><click:run_command:/npc text close><hover:show_text:Toggle sending messages when players get close>{0}<u>talk close</hover></click> | <click:run_command:/npc text random><hover:show_text:Toggle random talking>{1}<u>random</hover></click> | <click:suggest_command:/npc text speech bubbles duration ><hover:show_text:Set speech bubble duration>{2}<u>speech bubble duration</hover></click> | <click:run_command:/npc text speech bubbles><hover:show_text:Toggle showing text as holograms instead of chat messages>{2}<u>speech bubbles</hover></click> | <click:run_command:/npc text realistic looking><hover:show_text:Toggle requiring line of sight before speaking>{3}<u>realistic</hover></click>",
"citizens.editors.text.start-prompt" : "<click:suggest_command:add ><yellow><u>Add text</u></click> | <click:suggest_command:item ><hover:show_text:\"Set the talk item in hand pattern (set to <yellow>default</yellow> to clear)\"><yellow><u>item</hover></click> | <click:suggest_command:range ><hover:show_text:Set the talking range in blocks><yellow><u>range</hover></click> | <click:suggest_command:delay ><hover:show_text:Set the talking delay in ticks><yellow><u>delay</u></yellow></hover></click><br><click:run_command:/npc text close><hover:show_text:Toggle sending messages when players get close>{0}<u>talk close</hover></click> | <click:run_command:/npc text random><hover:show_text:Toggle random talking>{1}<u>random</hover></click> | <click:suggest_command:/npc text speech bubbles duration ><hover:show_text:Set speech bubble duration>{2}<u>speech bubble duration</hover></click> | <click:run_command:/npc text speech bubbles><hover:show_text:Toggle showing text as holograms instead of chat messages>{2}<u>speech bubbles</hover></click> | <click:run_command:/npc text realistic looking><hover:show_text:Toggle requiring line of sight before speaking>{3}<u>realistic</hover></click> | <click:run_command:/npc text send text to chat><hover:show_text:Toggle sending text to chat>{4}<u>send to chat</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.guided.added-available" : "Added a [[destination]] waypoint which the NPC will randomly pathfind between.",

View File

@ -84,6 +84,7 @@ import net.citizensnpcs.api.trait.TraitInfo;
import net.citizensnpcs.api.util.BoundingBox;
import net.citizensnpcs.api.util.EntityDim;
import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.api.util.SpigotUtil;
import net.citizensnpcs.api.util.SpigotUtil.InventoryViewAPI;
import net.citizensnpcs.nms.v1_20_R4.entity.AllayController;
import net.citizensnpcs.nms.v1_20_R4.entity.ArmadilloController;
@ -260,6 +261,14 @@ import net.citizensnpcs.util.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.ByteArrayTag;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.IntArrayTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.LongArrayTag;
import net.minecraft.nbt.NumericTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.Connection;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
@ -621,6 +630,35 @@ public class NMSImpl implements NMSBridge {
return shape.isEmpty() ? BoundingBox.EMPTY : NMSBoundingBox.wrap(shape.bounds());
}
@Override
public Map<String, Object> getComponentMap(org.bukkit.inventory.ItemStack item) {
if (META_COMPOUND_TAG == null) {
try {
META_COMPOUND_TAG = NMS.getGetter(Class.forName(
"org.bukkit.craftbukkit." + SpigotUtil.getMinecraftPackage() + ".inventory.CraftMetaItem"),
"customTag");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
Map<String, Object> base = Maps.newHashMap(NMSBridge.super.getComponentMap(item));
CompoundTag ct;
try {
ct = (CompoundTag) META_COMPOUND_TAG.invoke(item.getItemMeta());
} catch (Throwable e) {
e.printStackTrace();
return base;
}
if (ct == null)
return base;
Map<String, Object> custom = Maps.newHashMap();
for (String key : ct.getAllKeys()) {
custom.put(key, deserialiseNBT(ct.get(key)));
}
base.put("custom", custom);
return base;
}
@Override
public Location getDestination(org.bukkit.entity.Entity entity) {
Entity handle = getHandle(entity);
@ -2085,6 +2123,42 @@ public class NMSImpl implements NMSBridge {
}
}
private static Object deserialiseNBT(Tag tag) {
switch (tag.getId()) {
case Tag.TAG_COMPOUND:
CompoundTag ct = (CompoundTag) tag;
Map<String, Object> map = Maps.newHashMapWithExpectedSize(ct.size());
for (String key : ct.getAllKeys()) {
map.put(key, deserialiseNBT(ct.get(key)));
}
return map;
case Tag.TAG_LIST:
ListTag list = (ListTag) tag;
List<Object> res = Lists.newArrayList(list.size());
for (int i = 0; i < list.size(); i++) {
res.add(deserialiseNBT(list.get(i)));
}
return res;
case Tag.TAG_BYTE_ARRAY:
return ((ByteArrayTag) tag).getAsByteArray();
case Tag.TAG_INT_ARRAY:
return ((IntArrayTag) tag).getAsIntArray();
case Tag.TAG_LONG_ARRAY:
return ((LongArrayTag) tag).getAsLongArray();
case Tag.TAG_STRING:
return ((StringTag) tag).getAsString();
case Tag.TAG_ANY_NUMERIC:
case Tag.TAG_LONG:
case Tag.TAG_FLOAT:
case Tag.TAG_DOUBLE:
case Tag.TAG_INT:
case Tag.TAG_BYTE:
case Tag.TAG_SHORT:
return ((NumericTag) tag).getAsNumber();
}
throw new IllegalArgumentException();
}
public static void flyingMoveLogic(LivingEntity entity, Vec3 vec3d) {
if (entity.isEffectiveAi() || entity.isControlledByLocalInstance()) {
double d0 = 0.08D;
@ -2564,6 +2638,7 @@ public class NMSImpl implements NMSBridge {
private static final MethodHandle ATTRIBUTE_PROVIDER_MAP_SETTER = NMS.getFirstFinalSetter(AttributeSupplier.class,
Map.class);
private static final MethodHandle ATTRIBUTE_SUPPLIER = NMS.getFirstGetter(AttributeMap.class,
AttributeSupplier.class);
private static final MethodHandle AVAILABLE_BEHAVIORS_BY_PRIORITY = NMS.getGetter(Brain.class, "f");
@ -2610,6 +2685,7 @@ public class NMSImpl implements NMSBridge {
private static EntityDataAccessor<Float> INTERACTION_WIDTH = null;
private static final MethodHandle JUMP_FIELD = NMS.getGetter(LivingEntity.class, "bn");
private static final MethodHandle LOOK_CONTROL_SETTER = NMS.getFirstSetter(Mob.class, LookControl.class);
private static MethodHandle META_COMPOUND_TAG;
private static final MethodHandle MINECRAFT_CLIENT = NMS.getFirstGetter(YggdrasilMinecraftSessionService.class,
MinecraftClient.class);
private static final MethodHandle MOVE_CONTROLLER_OPERATION = NMS.getSetter(MoveControl.class, "k");

View File

@ -84,6 +84,7 @@ import net.citizensnpcs.api.trait.TraitInfo;
import net.citizensnpcs.api.util.BoundingBox;
import net.citizensnpcs.api.util.EntityDim;
import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.api.util.SpigotUtil;
import net.citizensnpcs.api.util.SpigotUtil.InventoryViewAPI;
import net.citizensnpcs.nms.v1_21_R1.entity.AllayController;
import net.citizensnpcs.nms.v1_21_R1.entity.ArmadilloController;
@ -260,6 +261,14 @@ import net.citizensnpcs.util.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.ByteArrayTag;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.IntArrayTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.LongArrayTag;
import net.minecraft.nbt.NumericTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.Connection;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
@ -601,6 +610,35 @@ public class NMSImpl implements NMSBridge {
return shape.isEmpty() ? BoundingBox.EMPTY : NMSBoundingBox.wrap(shape.bounds());
}
@Override
public Map<String, Object> getComponentMap(org.bukkit.inventory.ItemStack item) {
if (META_COMPOUND_TAG == null) {
try {
META_COMPOUND_TAG = NMS.getGetter(Class.forName(
"org.bukkit.craftbukkit." + SpigotUtil.getMinecraftPackage() + ".inventory.CraftMetaItem"),
"customTag");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
Map<String, Object> base = Maps.newHashMap(NMSBridge.super.getComponentMap(item));
CompoundTag ct;
try {
ct = (CompoundTag) META_COMPOUND_TAG.invoke(item.getItemMeta());
} catch (Throwable e) {
e.printStackTrace();
return base;
}
if (ct == null)
return base;
Map<String, Object> custom = Maps.newHashMap();
for (String key : ct.getAllKeys()) {
custom.put(key, deserialiseNBT(ct.get(key)));
}
base.put("custom", custom);
return base;
}
@Override
public Location getDestination(org.bukkit.entity.Entity entity) {
Entity handle = getHandle(entity);
@ -2079,6 +2117,42 @@ public class NMSImpl implements NMSBridge {
}
}
private static Object deserialiseNBT(Tag tag) {
switch (tag.getId()) {
case Tag.TAG_COMPOUND:
CompoundTag ct = (CompoundTag) tag;
Map<String, Object> map = Maps.newHashMapWithExpectedSize(ct.size());
for (String key : ct.getAllKeys()) {
map.put(key, deserialiseNBT(ct.get(key)));
}
return map;
case Tag.TAG_LIST:
ListTag list = (ListTag) tag;
List<Object> res = Lists.newArrayList(list.size());
for (int i = 0; i < list.size(); i++) {
res.add(deserialiseNBT(list.get(i)));
}
return res;
case Tag.TAG_BYTE_ARRAY:
return ((ByteArrayTag) tag).getAsByteArray();
case Tag.TAG_INT_ARRAY:
return ((IntArrayTag) tag).getAsIntArray();
case Tag.TAG_LONG_ARRAY:
return ((LongArrayTag) tag).getAsLongArray();
case Tag.TAG_STRING:
return ((StringTag) tag).getAsString();
case Tag.TAG_ANY_NUMERIC:
case Tag.TAG_LONG:
case Tag.TAG_FLOAT:
case Tag.TAG_DOUBLE:
case Tag.TAG_INT:
case Tag.TAG_BYTE:
case Tag.TAG_SHORT:
return ((NumericTag) tag).getAsNumber();
}
throw new IllegalArgumentException();
}
public static void flyingMoveLogic(LivingEntity entity, Vec3 vec3d) {
if (entity.isEffectiveAi() || entity.isControlledByLocalInstance()) {
double d0 = 0.08D;
@ -2541,9 +2615,12 @@ public class NMSImpl implements NMSBridge {
}
private static final MethodHandle ARMADILLO_SCUTE_TIME = NMS.getSetter(Armadillo.class, "cn");
private static final MethodHandle ATTRIBUTE_PROVIDER_MAP = NMS.getFirstGetter(AttributeSupplier.class, Map.class);
private static final MethodHandle ATTRIBUTE_PROVIDER_MAP_SETTER = NMS.getFirstFinalSetter(AttributeSupplier.class,
Map.class);
private static final MethodHandle ATTRIBUTE_SUPPLIER = NMS.getFirstGetter(AttributeMap.class,
AttributeSupplier.class);
private static final MethodHandle AVAILABLE_BEHAVIORS_BY_PRIORITY = NMS.getGetter(Brain.class, "f");
@ -2589,6 +2666,7 @@ public class NMSImpl implements NMSBridge {
private static EntityDataAccessor<Float> INTERACTION_WIDTH = null;
private static final MethodHandle JUMP_FIELD = NMS.getGetter(LivingEntity.class, "bn");
private static final MethodHandle LOOK_CONTROL_SETTER = NMS.getFirstSetter(Mob.class, LookControl.class);
private static MethodHandle META_COMPOUND_TAG;
private static final MethodHandle MINECRAFT_CLIENT = NMS.getFirstGetter(YggdrasilMinecraftSessionService.class,
MinecraftClient.class);
private static final MethodHandle MOVE_CONTROLLER_OPERATION = NMS.getSetter(MoveControl.class, "k");