Remove VocalChord/SpeechFactory

This commit is contained in:
fullwall 2023-04-20 23:42:48 +08:00
parent 78c025a061
commit b53955ebb2
9 changed files with 123 additions and 332 deletions

View File

@ -41,7 +41,7 @@ import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.CitizensPlugin;
import net.citizensnpcs.api.LocationLookup;
import net.citizensnpcs.api.NMSHelper;
import net.citizensnpcs.api.ai.speech.SpeechFactory;
import net.citizensnpcs.api.ai.speech.SpeechContext;
import net.citizensnpcs.api.command.CommandManager;
import net.citizensnpcs.api.command.Injector;
import net.citizensnpcs.api.event.CitizensDisableEvent;
@ -75,7 +75,6 @@ import net.citizensnpcs.npc.CitizensNPCRegistry;
import net.citizensnpcs.npc.CitizensTraitFactory;
import net.citizensnpcs.npc.NPCSelector;
import net.citizensnpcs.npc.Template;
import net.citizensnpcs.npc.ai.speech.CitizensSpeechFactory;
import net.citizensnpcs.npc.profile.ProfileFetcher;
import net.citizensnpcs.npc.skin.Skin;
import net.citizensnpcs.trait.ClickRedirectTrait;
@ -150,7 +149,6 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
private NPCDataStore saves;
private NPCSelector selector;
private StoredShops shops;
private CitizensSpeechFactory speechFactory;
private final Map<String, NPCRegistry> storedRegistries = Maps.newHashMap();
private CitizensTraitFactory traitFactory;
@ -287,11 +285,6 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
return shops;
}
@Override
public SpeechFactory getSpeechFactory() {
return speechFactory;
}
@Override
public TraitFactory getTraitFactory() {
return traitFactory;
@ -408,7 +401,6 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
locationLookup = new LocationLookup();
locationLookup.runTaskTimer(CitizensAPI.getPlugin(), 0, 5);
speechFactory = new CitizensSpeechFactory();
npcRegistry = new CitizensNPCRegistry(saves, "citizens");
traitFactory = new CitizensTraitFactory(this);
traitFactory.registerTrait(TraitInfo.create(ShopTrait.class).withSupplier(() -> {
@ -588,6 +580,11 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
}
}
@Override
public void talk(SpeechContext context) {
Util.talk(context);
}
private class CitizensLoadTask implements Runnable {
@Override
public void run() {

View File

@ -83,7 +83,6 @@ import net.citizensnpcs.api.trait.trait.MobType;
import net.citizensnpcs.api.trait.trait.Owner;
import net.citizensnpcs.api.trait.trait.PlayerFilter;
import net.citizensnpcs.api.trait.trait.Spawned;
import net.citizensnpcs.api.trait.trait.Speech;
import net.citizensnpcs.api.util.EntityDim;
import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.api.util.Paginator;
@ -2874,7 +2873,7 @@ public class NPCCommands {
@Command(
aliases = { "npc" },
usage = "speak [message] --target [npcid|player name] --range (range to look for entities to speak to in blocks) (--type vocal_type)",
usage = "speak [message] --target [npcid|player name] --range (range to look for entities to speak to in blocks)",
desc = "Says a message from the NPC",
modifiers = { "speak" },
min = 2,
@ -2883,12 +2882,6 @@ public class NPCCommands {
@Flag("target") String target, @Flag("range") Float range) throws CommandException {
String message = args.getJoinedStrings(1);
if (message.length() <= 0) {
Messaging.send(sender, "Default Vocal Chord for " + npc.getName() + ": "
+ npc.getOrAddTrait(Speech.class).getDefaultVocalChord());
return;
}
SpeechContext context = new SpeechContext(message);
if (target != null) {
@ -2912,11 +2905,7 @@ public class NPCCommands {
});
}
if (type == null || !CitizensAPI.getSpeechFactory().isRegistered(type)) {
type = npc.getOrAddTrait(Speech.class).getDefaultVocalChord();
}
npc.getDefaultSpeechController().speak(context, type);
npc.getDefaultSpeechController().speak(context);
}
@Command(

View File

@ -19,7 +19,6 @@ import net.citizensnpcs.api.trait.trait.MobType;
import net.citizensnpcs.api.trait.trait.Owner;
import net.citizensnpcs.api.trait.trait.PlayerFilter;
import net.citizensnpcs.api.trait.trait.Spawned;
import net.citizensnpcs.api.trait.trait.Speech;
import net.citizensnpcs.trait.Age;
import net.citizensnpcs.trait.Anchors;
import net.citizensnpcs.trait.ArmorStandTrait;
@ -126,7 +125,6 @@ public class CitizensTraitFactory implements TraitFactory {
registerTrait(TraitInfo.create(SneakTrait.class));
registerTrait(TraitInfo.create(SlimeSize.class));
registerTrait(TraitInfo.create(Spawned.class));
registerTrait(TraitInfo.create(Speech.class));
registerTrait(TraitInfo.create(Text.class));
registerTrait(TraitInfo.create(Waypoints.class));
registerTrait(TraitInfo.create(WitherTrait.class));

View File

@ -1,125 +0,0 @@
package net.citizensnpcs.npc.ai.speech;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.entity.Entity;
import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.ai.speech.SpeechContext;
import net.citizensnpcs.api.ai.speech.Talkable;
import net.citizensnpcs.api.ai.speech.VocalChord;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.util.Messaging;
public class Chat implements VocalChord {
@Override
public String getName() {
return "chat";
}
@Override
public void talk(SpeechContext context) {
if (context.getTalker() == null)
return;
NPC npc = CitizensAPI.getNPCRegistry().getNPC(context.getTalker().getEntity());
if (npc == null)
return;
// chat to the world with CHAT_FORMAT and CHAT_RANGE settings
if (!context.hasRecipients()) {
String text = Setting.CHAT_FORMAT.asString().replace("<text>", context.getMessage());
talkToBystanders(npc, text, context);
return;
}
// Assumed recipients at this point
else if (context.size() <= 1) {
String text = Setting.CHAT_FORMAT_TO_TARGET.asString().replace("<text>", context.getMessage());
String targetName = "";
// For each recipient
for (Talkable talkable : context) {
talkable.talkTo(context, text, this);
targetName = talkable.getName();
}
// Check if bystanders hear targeted chat
if (!Setting.CHAT_BYSTANDERS_HEAR_TARGETED_CHAT.asBoolean())
return;
// Format message with config setting and send to bystanders
String bystanderText = Setting.CHAT_FORMAT_TO_BYSTANDERS.asString().replace("<target>", targetName)
.replace("<text>", context.getMessage());
talkToBystanders(npc, bystanderText, context);
return;
}
else { // Multiple recipients
String text = Setting.CHAT_FORMAT_TO_TARGET.asString().replace("<text>", context.getMessage());
List<String> targetNames = new ArrayList<String>();
// Talk to each recipient
for (Talkable talkable : context) {
talkable.talkTo(context, text, this);
targetNames.add(talkable.getName());
}
if (!Setting.CHAT_BYSTANDERS_HEAR_TARGETED_CHAT.asBoolean())
return;
String targets = "";
int max = Setting.CHAT_MAX_NUMBER_OF_TARGETS.asInt();
String[] format = Setting.CHAT_MULTIPLE_TARGETS_FORMAT.asString().split("\\|");
if (format.length != 4)
Messaging.severe("npc.chat.options.multiple-targets-format invalid!");
if (max == 1) {
targets = format[0].replace("<target>", targetNames.get(0)) + format[3];
} else if (max == 2 || targetNames.size() == 2) {
if (targetNames.size() == 2) {
targets = format[0].replace("<target>", targetNames.get(0))
+ format[2].replace("<target>", targetNames.get(1));
} else
targets = format[0].replace("<target>", targetNames.get(0))
+ format[1].replace("<target>", targetNames.get(1)) + format[3];
} else if (max >= 3) {
targets = format[0].replace("<target>", targetNames.get(0));
int x = 1;
for (x = 1; x < max - 1; x++) {
if (targetNames.size() - 1 == x)
break;
targets = targets + format[1].replace("<npc>", targetNames.get(x));
}
if (targetNames.size() == max) {
targets = targets + format[2].replace("<npc>", targetNames.get(x));
} else
targets = targets + format[3];
}
String bystanderText = Setting.CHAT_FORMAT_WITH_TARGETS_TO_BYSTANDERS.asString()
.replace("<targets>", targets).replace("<text>", context.getMessage());
talkToBystanders(npc, bystanderText, context);
}
}
private void talkToBystanders(NPC npc, String text, SpeechContext context) {
// Get list of nearby entities
List<Entity> bystanderEntities = npc.getEntity().getNearbyEntities(Setting.CHAT_RANGE.asDouble(),
Setting.CHAT_RANGE.asDouble(), Setting.CHAT_RANGE.asDouble());
for (Entity bystander : bystanderEntities) {
boolean shouldTalk = true;
if (!Setting.TALK_CLOSE_TO_NPCS.asBoolean() && CitizensAPI.getNPCRegistry().isNPC(bystander)) {
shouldTalk = false;
}
if (context.hasRecipients()) {
for (Talkable target : context) {
if (target.getEntity().equals(bystander)) {
shouldTalk = false;
break;
}
}
}
if (shouldTalk) {
new TalkableEntity(bystander).talkNear(context, text, this);
}
}
}
}

View File

@ -1,88 +0,0 @@
package net.citizensnpcs.npc.ai.speech;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.bukkit.entity.Entity;
import com.google.common.base.Preconditions;
import net.citizensnpcs.api.ai.speech.SpeechFactory;
import net.citizensnpcs.api.ai.speech.Talkable;
import net.citizensnpcs.api.ai.speech.VocalChord;
public class CitizensSpeechFactory implements SpeechFactory {
private final Map<String, Class<? extends VocalChord>> registered = new HashMap<String, Class<? extends VocalChord>>();
public CitizensSpeechFactory() {
register(Chat.class, "chat");
}
@Override
public VocalChord getVocalChord(Class<? extends VocalChord> clazz) {
Preconditions.checkNotNull(clazz, "class cannot be null");
// Return a new instance of the VocalChord specified
try {
return clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
@Override
public VocalChord getVocalChord(String name) {
Preconditions.checkNotNull(name, "name cannot be null");
// Check if VocalChord name is a registered type
if (name.equalsIgnoreCase("chat")) {
return new Chat();
}
if (isRegistered(name)) {
// Return a new instance of the VocalChord specified
try {
return registered.get(name.toLowerCase()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return null;
}
@Override
public String getVocalChordName(Class<? extends VocalChord> clazz) {
// Get the name of a VocalChord class that has been registered
for (Entry<String, Class<? extends VocalChord>> vocalChord : registered.entrySet()) {
if (vocalChord.getValue() == clazz) {
return vocalChord.getKey();
}
}
return null;
}
@Override
public boolean isRegistered(String name) {
Preconditions.checkNotNull(name, "name cannot be null");
return registered.containsKey(name.toLowerCase());
}
@Override
public Talkable newTalkableEntity(Entity entity) {
if (entity == null)
return null;
return new TalkableEntity(entity);
}
@Override
public void register(Class<? extends VocalChord> clazz, String name) {
Preconditions.checkNotNull(name, "info cannot be null");
Preconditions.checkNotNull(clazz, "vocalchord cannot be null");
if (registered.containsKey(name.toLowerCase()))
throw new IllegalArgumentException("vocalchord name already registered");
registered.put(name.toLowerCase(), clazz);
}
}

View File

@ -1,90 +0,0 @@
package net.citizensnpcs.npc.ai.speech;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.ai.speech.SpeechContext;
import net.citizensnpcs.api.ai.speech.Talkable;
import net.citizensnpcs.api.ai.speech.VocalChord;
import net.citizensnpcs.api.ai.speech.event.SpeechBystanderEvent;
import net.citizensnpcs.api.ai.speech.event.SpeechTargetedEvent;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.util.Messaging;
public class TalkableEntity implements Talkable {
private final Entity entity;
public TalkableEntity(Entity entity) {
this.entity = entity;
}
public TalkableEntity(NPC npc) {
entity = npc.getEntity();
}
/**
* Used to compare a LivingEntity to this TalkableEntity
*
* @return 0 if the Entities are the same, 1 if they are not, -1 if the object compared is not a valid LivingEntity
*/
@Override
public int compareTo(Object o) {
if (!(o instanceof Entity)) {
return -1;
// If NPC and matches, return 0
} else if (CitizensAPI.getNPCRegistry().isNPC((Entity) o) && CitizensAPI.getNPCRegistry().isNPC(entity)
&& CitizensAPI.getNPCRegistry().getNPC((Entity) o).getUniqueId()
.equals(CitizensAPI.getNPCRegistry().getNPC(entity).getUniqueId())) {
return 0;
} else if (entity.equals(o)) {
return 0;
} else {
return 1;
}
}
@Override
public Entity getEntity() {
return entity;
}
@Override
public String getName() {
if (CitizensAPI.getNPCRegistry().isNPC(entity)) {
return CitizensAPI.getNPCRegistry().getNPC(entity).getFullName();
} else if (entity instanceof Player) {
return ((Player) entity).getName();
} else {
return entity.getType().name().replace("_", " ");
}
}
private void talk(NPC npc, String message) {
if (!CitizensAPI.getNPCRegistry().isNPC(entity)) {
Messaging.sendWithNPCColorless(entity, message, npc);
}
}
@Override
public void talkNear(SpeechContext context, String text, VocalChord vocalChord) {
SpeechBystanderEvent event = new SpeechBystanderEvent(this, context, text, vocalChord);
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled())
return;
NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getContext().getTalker().getEntity());
talk(npc, event.getMessage());
}
@Override
public void talkTo(SpeechContext context, String text, VocalChord vocalChord) {
SpeechTargetedEvent event = new SpeechTargetedEvent(this, context, text, vocalChord);
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled())
return;
NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getContext().getTalker().getEntity());
talk(npc, event.getMessage());
}
}

View File

@ -103,9 +103,9 @@ public class Skin {
String defaultSkinName = ChatColor.stripColor(npc.getName()).toLowerCase();
if (npc.hasTrait(SkinTrait.class) && this.skinName.equals(defaultSkinName)
&& !npc.getOrAddTrait(SkinTrait.class).fetchDefaultSkin()) {
&& !npc.getOrAddTrait(SkinTrait.class).fetchDefaultSkin())
return false;
}
if (hasFetched) {
return true;
} else {

View File

@ -10,6 +10,7 @@ import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.trait.TraitName;
import net.citizensnpcs.api.util.DataKey;
import net.citizensnpcs.api.util.Placeholders;
import net.citizensnpcs.npc.skin.Skin;
import net.citizensnpcs.npc.skin.SkinnableEntity;
import net.md_5.bungee.api.ChatColor;
@ -162,13 +163,13 @@ public class SkinTrait extends Trait {
setSkinNameInternal(skinName);
String json = new String(BaseEncoding.base64().decode(data), Charsets.UTF_8);
if (!json.contains("textures")) {
if (!json.contains("textures"))
throw new IllegalArgumentException("Invalid texture data");
}
this.signature = signature;
this.textureRaw = data;
this.updateSkins = false;
npc.data().setPersistent("cached-skin-uuid-name", skinName.toLowerCase());
npc.data().setPersistent(Skin.CACHED_SKIN_UUID_NAME_METADATA, skinName.toLowerCase());
onSkinChange(false);
}

View File

@ -2,6 +2,7 @@ package net.citizensnpcs.util;
import java.text.DecimalFormat;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
@ -33,7 +34,11 @@ import com.google.common.base.Splitter;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.ai.speech.SpeechContext;
import net.citizensnpcs.api.ai.speech.Talkable;
import net.citizensnpcs.api.ai.speech.TalkableEntity;
import net.citizensnpcs.api.event.NPCCollisionEvent;
import net.citizensnpcs.api.event.NPCPushEvent;
import net.citizensnpcs.api.npc.NPC;
@ -420,6 +425,109 @@ public class Util {
NMS.look(entity, yaw, pitch);
}
public static void talk(SpeechContext context) {
if (context.getTalker() == null)
return;
NPC npc = CitizensAPI.getNPCRegistry().getNPC(context.getTalker().getEntity());
if (npc == null)
return;
// chat to the world with CHAT_FORMAT and CHAT_RANGE settings
if (!context.hasRecipients()) {
String text = Setting.CHAT_FORMAT.asString().replace("<text>", context.getMessage());
talkToBystanders(npc, text, context);
return;
}
// Assumed recipients at this point
else if (context.size() <= 1) {
String text = Setting.CHAT_FORMAT_TO_TARGET.asString().replace("<text>", context.getMessage());
String targetName = "";
// For each recipient
for (Talkable talkable : context) {
talkable.talkTo(context, text);
targetName = talkable.getName();
}
// Check if bystanders hear targeted chat
if (!Setting.CHAT_BYSTANDERS_HEAR_TARGETED_CHAT.asBoolean())
return;
// Format message with config setting and send to bystanders
String bystanderText = Setting.CHAT_FORMAT_TO_BYSTANDERS.asString().replace("<target>", targetName)
.replace("<text>", context.getMessage());
talkToBystanders(npc, bystanderText, context);
return;
}
else { // Multiple recipients
String text = Setting.CHAT_FORMAT_TO_TARGET.asString().replace("<text>", context.getMessage());
List<String> targetNames = new ArrayList<String>();
// Talk to each recipient
for (Talkable talkable : context) {
talkable.talkTo(context, text);
targetNames.add(talkable.getName());
}
if (!Setting.CHAT_BYSTANDERS_HEAR_TARGETED_CHAT.asBoolean())
return;
String targets = "";
int max = Setting.CHAT_MAX_NUMBER_OF_TARGETS.asInt();
String[] format = Setting.CHAT_MULTIPLE_TARGETS_FORMAT.asString().split("\\|");
if (format.length != 4)
Messaging.severe("npc.chat.options.multiple-targets-format invalid!");
if (max == 1) {
targets = format[0].replace("<target>", targetNames.get(0)) + format[3];
} else if (max == 2 || targetNames.size() == 2) {
if (targetNames.size() == 2) {
targets = format[0].replace("<target>", targetNames.get(0))
+ format[2].replace("<target>", targetNames.get(1));
} else
targets = format[0].replace("<target>", targetNames.get(0))
+ format[1].replace("<target>", targetNames.get(1)) + format[3];
} else if (max >= 3) {
targets = format[0].replace("<target>", targetNames.get(0));
int x = 1;
for (x = 1; x < max - 1; x++) {
if (targetNames.size() - 1 == x)
break;
targets = targets + format[1].replace("<npc>", targetNames.get(x));
}
if (targetNames.size() == max) {
targets = targets + format[2].replace("<npc>", targetNames.get(x));
} else
targets = targets + format[3];
}
String bystanderText = Setting.CHAT_FORMAT_WITH_TARGETS_TO_BYSTANDERS.asString()
.replace("<targets>", targets).replace("<text>", context.getMessage());
talkToBystanders(npc, bystanderText, context);
}
}
private static void talkToBystanders(NPC npc, String text, SpeechContext context) {
// Get list of nearby entities
List<Entity> bystanderEntities = npc.getEntity().getNearbyEntities(Setting.CHAT_RANGE.asDouble(),
Setting.CHAT_RANGE.asDouble(), Setting.CHAT_RANGE.asDouble());
for (Entity bystander : bystanderEntities) {
boolean shouldTalk = true;
if (!Setting.TALK_CLOSE_TO_NPCS.asBoolean() && CitizensAPI.getNPCRegistry().isNPC(bystander)) {
shouldTalk = false;
}
if (context.hasRecipients()) {
for (Talkable target : context) {
if (target.getEntity().equals(bystander)) {
shouldTalk = false;
break;
}
}
}
if (shouldTalk) {
new TalkableEntity(bystander).talkNear(context, text);
}
}
}
public static int toTicks(Duration delay) {
return (int) (TimeUnit.MILLISECONDS.convert(delay.getSeconds(), TimeUnit.SECONDS)
+ TimeUnit.MILLISECONDS.convert(delay.getNano(), TimeUnit.NANOSECONDS)) / 50;
@ -429,6 +537,7 @@ public class Util {
private static final Scoreboard DUMMY_SCOREBOARD = Bukkit.getScoreboardManager().getNewScoreboard();
private static String MINECRAFT_REVISION;
private static final DecimalFormat TWO_DIGIT_DECIMAL = new DecimalFormat();
static {
TWO_DIGIT_DECIMAL.setMaximumFractionDigits(2);
}