Improve triggers editor.

This commit is contained in:
fullwall 2023-03-25 21:47:42 +08:00
parent bdc2264fc6
commit 4a55753409
28 changed files with 137 additions and 325 deletions

4
dist/pom.xml vendored
View File

@ -1,4 +1,6 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.citizensnpcs</groupId>

View File

@ -1,6 +1,5 @@
package net.citizensnpcs.commands;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import net.citizensnpcs.api.command.Command;
@ -48,15 +47,18 @@ public class EditorCommands {
desc = "Toggle the waypoint editor",
modifiers = { "path" },
min = 1,
max = 1,
flags = "*",
permission = "citizens.npc.edit.path")
@Requirements(selected = true, ownership = true)
public void path(CommandContext args, CommandSender player, NPC npc) {
public void path(CommandContext args, Player player, NPC npc) {
Editor editor = npc.getOrAddTrait(Waypoints.class).getEditor(player, args);
if (editor == null)
return;
Editor.enterOrLeave((Player) player, editor);
if (player.isConversing() && args.argsLength() > 1) {
player.acceptConversationInput(args.getJoinedStrings(1));
return;
}
Editor.enterOrLeave(player, editor);
}
@Command(

View File

@ -499,14 +499,8 @@ public class NPCCommands {
if (which == null)
throw new CommandException(Messages.NPC_COMMAND_INVALID_ERROR_MESSAGE,
Util.listValuesPretty(CommandTraitError.values()));
Player player = null;
if (args.argsLength() > 3) {
player = Bukkit.getPlayerExact(args.getString(3));
if (player == null) {
player = Bukkit.getPlayer(UUID.fromString(args.getString(3)));
}
}
commands.clearHistory(which, player);
commands.clearHistory(which, args.argsLength() > 3 ? args.getString(3) : args.getString(3));
Messaging.send(sender, Messages.NPC_COMMAND_ERRORS_CLEARED, Util.prettyEnum(which));
} else if (action.equalsIgnoreCase("sequential")) {
commands.setExecutionMode(commands.getExecutionMode() == ExecutionMode.SEQUENTIAL ? ExecutionMode.LINEAR
@ -690,7 +684,6 @@ public class NPCCommands {
if (args.hasFlag('t') || temporaryTicks != null) {
registry = temporaryRegistry;
}
if (item != null) {
ItemStack stack = new ItemStack(Material.STONE, 1);
try {

View File

@ -52,6 +52,7 @@ import net.citizensnpcs.trait.HologramTrait;
import net.citizensnpcs.trait.PacketNPC;
import net.citizensnpcs.trait.ScoreboardTrait;
import net.citizensnpcs.trait.SitTrait;
import net.citizensnpcs.trait.SkinLayers;
import net.citizensnpcs.trait.SneakTrait;
import net.citizensnpcs.util.ChunkCoord;
import net.citizensnpcs.util.Messages;
@ -300,6 +301,10 @@ public class CitizensNPC extends AbstractNPC {
entityController.create(at.clone(), this);
getEntity().setMetadata("NPC", new FixedMetadataValue(CitizensAPI.getPlugin(), true));
if (getEntity() instanceof SkinnableEntity && !hasTrait(SkinLayers.class)) {
((SkinnableEntity) getEntity()).setSkinFlags((byte) 0xFF);
}
Collection<Trait> onPreSpawn = traits.values();
for (Trait trait : onPreSpawn.toArray(new Trait[onPreSpawn.size()])) {
try {
@ -530,7 +535,6 @@ public class CitizensNPC extends AbstractNPC {
}
updateCounter = 0;
}
updateCustomNameVisibility();
if (isLiving) {

View File

@ -88,8 +88,8 @@ public class CitizensNPCRegistry implements NPCRegistry {
@Override
public NPC createNPCUsingItem(EntityType type, String name, ItemStack item) {
NPC npc = createNPC(type, name);
if (type == EntityType.DROPPED_ITEM || type == EntityType.FALLING_BLOCK || type == EntityType.GLOW_ITEM_FRAME
|| type == EntityType.ITEM_FRAME) {
if (type == EntityType.DROPPED_ITEM || type == EntityType.FALLING_BLOCK || type == EntityType.ITEM_FRAME
|| type.name().equals("GLOW_ITEM_FRAME") || type.name().equals("ITEM_DISPLAY")) {
npc.data().set(NPC.Metadata.ITEM_AMOUNT, item.getAmount());
npc.data().set(NPC.Metadata.ITEM_ID, item.getType().name());
npc.data().set(NPC.Metadata.ITEM_DATA, item.getData().getData());

View File

@ -118,7 +118,18 @@ public class CommandTrait extends Trait {
return action == null ? Transaction.success() : action.take(player);
}
public void clearHistory(CommandTraitError which, Player who) {
public void clearHistory(CommandTraitError which, String raw) {
if (which == CommandTraitError.ON_GLOBAL_COOLDOWN && raw != null) {
globalCooldowns.remove(BaseEncoding.base64().encode(raw.getBytes()));
return;
}
Player who = null;
if (raw != null) {
who = Bukkit.getPlayerExact(raw);
if (who == null) {
who = Bukkit.getPlayer(UUID.fromString(raw));
}
}
Collection<PlayerNPCCommand> toClear = Lists.newArrayList();
if (who != null) {
toClear.add(playerTracking.get(who.getUniqueId()));

View File

@ -102,6 +102,7 @@ public class HologramTrait extends Trait {
if (Setting.PACKET_HOLOGRAMS.asBoolean()) {
hologramNPC.addTrait(PacketNPC.class);
}
hologramNPC.spawn(currentLoc.clone().add(0,
getEntityHeight()
+ (direction == HologramDirection.BOTTOM_UP ? heightOffset : getMaxHeight() - heightOffset),

View File

@ -13,8 +13,6 @@ import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.conversations.Conversation;
import org.bukkit.conversations.ConversationAbandonedEvent;
import org.bukkit.conversations.ConversationAbandonedListener;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.block.Action;
@ -330,12 +328,7 @@ public class LinearWaypointProvider implements EnumerableWaypointProvider {
@Override
public void run() {
conversation = TriggerEditPrompt.start(player, LinearWaypointEditor.this);
conversation.addConversationAbandonedListener(new ConversationAbandonedListener() {
@Override
public void conversationAbandoned(ConversationAbandonedEvent event) {
conversation = null;
}
});
conversation.addConversationAbandonedListener(e -> conversation = null);
}
});
} else if (message.equalsIgnoreCase("clear")) {
@ -348,21 +341,13 @@ public class LinearWaypointProvider implements EnumerableWaypointProvider {
});
} else if (message.equalsIgnoreCase("toggle path") || message.equalsIgnoreCase("markers")) {
event.setCancelled(true);
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), new Runnable() {
@Override
public void run() {
togglePath();
}
});
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> togglePath());
} else if (message.equalsIgnoreCase("cycle")) {
event.setCancelled(true);
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), new Runnable() {
@Override
public void run() {
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> {
cycle = !cycle;
Messaging.sendTr(event.getPlayer(), cycle ? Messages.LINEAR_WAYPOINT_EDITOR_CYCLE_SET
: Messages.LINEAR_WAYPOINT_EDITOR_CYCLE_UNSET);
}
});
}
}

View File

@ -173,17 +173,12 @@ public class WanderWaypointProvider
});
} else if (message.startsWith("delay")) {
event.setCancelled(true);
try {
delay = Integer.parseInt(message.split(" ")[1]);
delay = Util.parseTicks(message.split(" ")[1]);
if (currentGoal != null) {
currentGoal.setDelay(delay);
}
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(),
() -> Messaging.sendTr(sender, Messages.WANDER_WAYPOINTS_DELAY_SET, delay));
} catch (Exception e) {
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(),
() -> Messaging.sendErrorTr(sender, Messages.WANDER_WAYPOINTS_INVALID_DELAY));
}
} else if (message.startsWith("worldguardregion")) {
event.setCancelled(true);
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> {

View File

@ -47,8 +47,12 @@ public class Waypoint implements Locatable {
public void describeTriggers(CommandSender sender) {
String base = "";
for (WaypointTrigger trigger : getTriggers()) {
base += "\n - " + trigger.description();
if (triggers == null)
return;
for (int i = 0; i < triggers.size(); i++) {
base += "\n - " + triggers.get(i).description()
+ " [<hover:show_text:Remove trigger><click:run_command:/npc path remove_trigger " + i
+ "><u><red>-</click></hover>]";
}
Messaging.sendTr(sender, Messages.WAYPOINT_TRIGGER_LIST, base);
}

View File

@ -35,7 +35,7 @@ public class ChatTrigger implements WaypointTrigger {
@Override
public void onWaypointReached(NPC npc, Location waypoint) {
if (radius < 0) {
if (radius <= 0) {
for (Player player : npc.getEntity().getWorld().getPlayers()) {
for (String line : lines) {
Messaging.send(player, line);

View File

@ -7,6 +7,7 @@ import org.bukkit.conversations.ConversationContext;
import org.bukkit.conversations.Prompt;
import org.bukkit.conversations.StringPrompt;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import net.citizensnpcs.api.util.Messaging;
@ -48,7 +49,12 @@ public class ChatTriggerPrompt extends StringPrompt implements WaypointTriggerPr
@Override
public String getPromptText(ConversationContext context) {
if (context.getSessionData("said") == Boolean.TRUE) {
Messaging.send((CommandSender) context.getForWhom(),
"Current lines:<br>- " + Joiner.on("<br>- ").join(lines));
} else {
Messaging.sendTr((CommandSender) context.getForWhom(), Messages.CHAT_TRIGGER_PROMPT);
}
return "";
}
}

View File

@ -1,32 +1,30 @@
package net.citizensnpcs.trait.waypoint.triggers;
import org.bukkit.command.CommandSender;
import org.bukkit.conversations.ConversationContext;
import org.bukkit.conversations.NumericPrompt;
import org.bukkit.conversations.Prompt;
import org.bukkit.conversations.StringPrompt;
import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.util.Messages;
import net.citizensnpcs.util.Util;
public class DelayTriggerPrompt extends NumericPrompt implements WaypointTriggerPrompt {
public class DelayTriggerPrompt extends StringPrompt implements WaypointTriggerPrompt {
@Override
protected Prompt acceptValidatedInput(ConversationContext context, Number input) {
int delay = Math.max(input.intValue(), 0);
public Prompt acceptInput(ConversationContext context, String input) {
int delay = Math.max(0, Util.parseTicks(input));
context.setSessionData(WaypointTriggerPrompt.CREATED_TRIGGER_KEY, new DelayTrigger(delay));
return (Prompt) context.getSessionData(WaypointTriggerPrompt.RETURN_PROMPT_KEY);
}
@Override
public WaypointTrigger createFromShortInput(ConversationContext context, String input) {
try {
int delay = Math.max(Integer.parseInt(input), 0);
return new DelayTrigger(delay);
} catch (NumberFormatException ex) {
return null;
}
return new DelayTrigger(Math.max(0, Util.parseTicks(input)));
}
@Override
public String getPromptText(ConversationContext context) {
return Messaging.tr(Messages.DELAY_TRIGGER_PROMPT);
Messaging.sendTr((CommandSender) context.getForWhom(), Messages.DELAY_TRIGGER_PROMPT);
return "";
}
}

View File

@ -1,5 +1,6 @@
package net.citizensnpcs.trait.waypoint.triggers;
import org.bukkit.command.CommandSender;
import org.bukkit.conversations.ConversationContext;
import org.bukkit.conversations.NumericPrompt;
import org.bukkit.conversations.Prompt;
@ -27,6 +28,7 @@ public class SpeedTriggerPrompt extends NumericPrompt implements WaypointTrigger
@Override
public String getPromptText(ConversationContext context) {
return Messaging.tr(Messages.SPEED_TRIGGER_PROMPT);
Messaging.sendTr((CommandSender) context.getForWhom(), Messages.SPEED_TRIGGER_PROMPT);
return "";
}
}

View File

@ -55,7 +55,8 @@ public class TeleportTriggerPrompt extends RegexPrompt implements WaypointTrigge
@Override
public String getPromptText(ConversationContext context) {
return Messaging.tr(Messages.WAYPOINT_TRIGGER_TELEPORT_PROMPT);
Messaging.sendTr((CommandSender) context.getForWhom(), Messages.WAYPOINT_TRIGGER_TELEPORT_PROMPT);
return "";
}
private static final Pattern PATTERN = Pattern.compile("here|back|[\\p{L}]+?:[0-9]+?:[0-9]+?:[0-9]+?",

View File

@ -39,7 +39,8 @@ public class TriggerAddPrompt extends StringPrompt {
WaypointTrigger returned = ((WaypointTriggerPrompt) prompt).createFromShortInput(context, extraInput);
if (returned != null) {
context.setSessionData(WaypointTriggerPrompt.CREATED_TRIGGER_KEY, returned);
return this;
context.setSessionData("said", false);
return (Prompt) context.getSessionData("previous");
}
}
return prompt;
@ -47,23 +48,12 @@ public class TriggerAddPrompt extends StringPrompt {
@Override
public String getPromptText(ConversationContext context) {
WaypointTrigger returned = (WaypointTrigger) context.getSessionData(WaypointTriggerPrompt.CREATED_TRIGGER_KEY);
if (returned != null) {
if (editor.getCurrentWaypoint() != null) {
editor.getCurrentWaypoint().addTrigger(returned);
context.setSessionData(WaypointTriggerPrompt.CREATED_TRIGGER_KEY, null);
Messaging.sendTr((CommandSender) context.getForWhom(), Messages.WAYPOINT_TRIGGER_ADDED_SUCCESSFULLY,
returned.description());
editor.getCurrentWaypoint().describeTriggers((CommandSender) context.getForWhom());
} else {
Messaging.sendErrorTr((CommandSender) context.getForWhom(), Messages.WAYPOINT_TRIGGER_EDITOR_INACTIVE);
}
}
if (context.getSessionData("said") == Boolean.TRUE)
return "";
context.setSessionData("said", true);
context.setSessionData(WaypointTriggerPrompt.RETURN_PROMPT_KEY, this);
return Messaging.tr(Messages.WAYPOINT_TRIGGER_ADD_PROMPT, WaypointTriggerRegistry.describeValidTriggerNames());
context.setSessionData(WaypointTriggerPrompt.RETURN_PROMPT_KEY, context.getSessionData("previous"));
Messaging.sendTr((CommandSender) context.getForWhom(), Messages.WAYPOINT_TRIGGER_ADD_PROMPT,
WaypointTriggerRegistry.describeValidTriggerNames());
return "";
}
}

View File

@ -1,15 +1,17 @@
package net.citizensnpcs.trait.waypoint.triggers;
import java.util.List;
import org.bukkit.command.CommandSender;
import org.bukkit.conversations.Conversation;
import org.bukkit.conversations.ConversationAbandonedEvent;
import org.bukkit.conversations.ConversationAbandonedListener;
import org.bukkit.conversations.ConversationContext;
import org.bukkit.conversations.ConversationFactory;
import org.bukkit.conversations.Prompt;
import org.bukkit.conversations.StringPrompt;
import org.bukkit.entity.Player;
import com.google.common.primitives.Ints;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.trait.waypoint.Waypoint;
@ -26,43 +28,50 @@ public class TriggerEditPrompt extends StringPrompt {
@Override
public Prompt acceptInput(ConversationContext context, String input) {
input = input.toLowerCase().trim();
if (input.startsWith("remove_trigger")) {
Waypoint waypoint = editor.getCurrentWaypoint();
List<WaypointTrigger> triggers = waypoint.getTriggers();
int idx = Ints.tryParse(input.replaceFirst("remove_trigger\\s*", ""));
if (idx < triggers.size()) {
triggers.remove(idx);
}
return this;
}
if (input.contains("add")) {
context.setSessionData("said", false);
return new TriggerAddPrompt(editor);
}
if (input.contains("remove")) {
context.setSessionData("said", false);
return new TriggerRemovePrompt(editor);
}
return this;
}
@Override
public String getPromptText(ConversationContext context) {
context.setSessionData("previous", this);
if (context.getSessionData("said") == Boolean.TRUE)
return "";
context.setSessionData("said", true);
String base = "";
WaypointTrigger returned = (WaypointTrigger) context.getSessionData(WaypointTriggerPrompt.CREATED_TRIGGER_KEY);
if (returned != null) {
if (editor.getCurrentWaypoint() != null) {
Waypoint waypoint = editor.getCurrentWaypoint();
for (WaypointTrigger trigger : waypoint.getTriggers()) {
base += "\n - " + trigger.description();
editor.getCurrentWaypoint().addTrigger(returned);
Messaging.sendTr((CommandSender) context.getForWhom(), Messages.WAYPOINT_TRIGGER_ADDED_SUCCESSFULLY,
returned.description());
} else {
Messaging.sendErrorTr((CommandSender) context.getForWhom(), Messages.WAYPOINT_TRIGGER_EDITOR_INACTIVE);
}
context.setSessionData(WaypointTriggerPrompt.CREATED_TRIGGER_KEY, null);
}
context.setSessionData("said", false);
context.setSessionData("previous", this);
Messaging.sendTr((CommandSender) context.getForWhom(), Messages.WAYPOINT_TRIGGER_EDITOR_PROMPT);
if (editor.getCurrentWaypoint() != null) {
editor.getCurrentWaypoint().describeTriggers((CommandSender) context.getForWhom());
}
Messaging.sendTr((CommandSender) context.getForWhom(), Messages.WAYPOINT_TRIGGER_EDITOR_PROMPT, base);
return "";
}
public static Conversation start(Player player, WaypointEditor editor) {
final Conversation conversation = new ConversationFactory(CitizensAPI.getPlugin()).withLocalEcho(false)
.addConversationAbandonedListener(new ConversationAbandonedListener() {
@Override
public void conversationAbandoned(ConversationAbandonedEvent event) {
event.getContext().getForWhom()
.sendRawMessage(Messaging.tr(Messages.WAYPOINT_TRIGGER_EDITOR_EXIT));
}
}).withEscapeSequence("exit").withEscapeSequence("triggers").withEscapeSequence("/npc path")
.addConversationAbandonedListener(event -> Messaging
.sendTr((CommandSender) event.getContext().getForWhom(), Messages.WAYPOINT_TRIGGER_EDITOR_EXIT))
.withEscapeSequence("exit").withEscapeSequence("triggers").withEscapeSequence("/npc path")
.withModality(false).withFirstPrompt(new TriggerEditPrompt(editor)).buildConversation(player);
conversation.begin();
return conversation;

View File

@ -1,67 +0,0 @@
package net.citizensnpcs.trait.waypoint.triggers;
import java.util.List;
import org.bukkit.command.CommandSender;
import org.bukkit.conversations.ConversationContext;
import org.bukkit.conversations.Prompt;
import org.bukkit.conversations.StringPrompt;
import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.trait.waypoint.WaypointEditor;
import net.citizensnpcs.util.Messages;
public class TriggerRemovePrompt extends StringPrompt {
private final WaypointEditor editor;
public TriggerRemovePrompt(WaypointEditor editor) {
this.editor = editor;
}
@Override
public Prompt acceptInput(ConversationContext context, String input) {
if (input.equalsIgnoreCase("back")) {
context.setSessionData("said", false);
return (Prompt) context.getSessionData("previous");
}
if (editor.getCurrentWaypoint() == null) {
Messaging.sendErrorTr((CommandSender) context.getForWhom(), Messages.WAYPOINT_TRIGGER_EDITOR_INACTIVE);
return this;
}
int index = 0;
try {
index = Math.max(0, Integer.parseInt(input) - 1);
} catch (NumberFormatException e) {
Messaging.sendErrorTr((CommandSender) context.getForWhom(),
Messages.WAYPOINT_TRIGGER_REMOVE_INVALID_NUMBER);
return this;
}
List<WaypointTrigger> triggers = editor.getCurrentWaypoint().getTriggers();
if (index >= triggers.size()) {
Messaging.sendErrorTr((CommandSender) context.getForWhom(),
Messages.WAYPOINT_TRIGGER_REMOVE_INDEX_OUT_OF_RANGE, triggers.size());
} else {
triggers.remove(index);
Messaging.sendTr((CommandSender) context.getForWhom(), Messages.WAYPOINT_TRIGGER_REMOVE_REMOVED, index + 1);
}
return this;
}
@Override
public String getPromptText(ConversationContext context) {
if (editor.getCurrentWaypoint() == null) {
Messaging.sendErrorTr((CommandSender) context.getForWhom(), Messages.WAYPOINT_TRIGGER_EDITOR_INACTIVE);
return "";
}
if (context.getSessionData("said") == Boolean.TRUE)
return "";
context.setSessionData("said", true);
String root = "";
int i = 1;
for (WaypointTrigger trigger : editor.getCurrentWaypoint().getTriggers()) {
root += String.format("<br> [[%d]]. " + trigger.description(), i++);
}
Messaging.sendTr((CommandSender) context.getForWhom(), Messages.WAYPOINT_TRIGGER_REMOVE_PROMPT, root);
return "";
}
}

View File

@ -453,10 +453,6 @@ public class Messages {
public static final String WAYPOINT_TRIGGER_EDITOR_INVALID_TRIGGER = "citizens.editors.waypoints.triggers.add.invalid-trigger";
public static final String WAYPOINT_TRIGGER_EDITOR_PROMPT = "citizens.editors.waypoints.triggers.main.prompt";
public static final String WAYPOINT_TRIGGER_LIST = "citizens.editors.waypoints.triggers.list";
public static final String WAYPOINT_TRIGGER_REMOVE_INDEX_OUT_OF_RANGE = "citizens.editors.waypoints.triggers.remove.index-out-of-range";
public static final String WAYPOINT_TRIGGER_REMOVE_INVALID_NUMBER = "citizens.editors.waypoints.triggers.remove.not-a-number";
public static final String WAYPOINT_TRIGGER_REMOVE_PROMPT = "citizens.editors.waypoints.triggers.remove.prompt";
public static final String WAYPOINT_TRIGGER_REMOVE_REMOVED = "citizens.editors.waypoints.triggers.remove.removed";
public static final String WAYPOINT_TRIGGER_TELEPORT_PROMPT = "citizens.editors.waypoints.triggers.teleport.prompt";
public static final String WOLF_TRAIT_UPDATED = "citizens.commands.wolf.traits-updated";
public static final String WORLD_NOT_FOUND = "citizens.commands.errors.missing-world";

View File

@ -1,113 +0,0 @@
package net.citizensnpcs.util;
import org.bukkit.util.EulerAngle;
public class Quaternion {
public final double w;
public final double x;
public final double y;
public final double z;
public Quaternion(double x, double y, double z, double w) {
this.w = w;
this.x = x;
this.y = y;
this.z = z;
}
public double dot(Quaternion b) {
return x * b.x + y * b.y + z * b.z + w * b.w;
}
public double length() {
return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
}
public Quaternion mul(double m) {
return new Quaternion(x * m, y * m, z * m, w * m);
}
public Quaternion norm() {
double length = length();
if (length > 0.0001) {
double i = fastisqrt(length);
return new Quaternion(i * x, i * y, i * z, i * w);
}
return ZERO;
}
public EulerAngle toEuler() {
double w2 = w * w;
double x2 = x * x;
double y2 = y * y;
double z2 = z * z;
double length = x2 + y2 + z2 + w2;
double test = x * y + z * w;
if (test > 0.499 * length) {
return new EulerAngle(0, 2 * Math.atan2(x, w), Math.PI / 2);
} else if (test < -0.499 * length) {
return new EulerAngle(0, -2 * Math.atan2(x, w), -(Math.PI / 2));
} else {
return new EulerAngle(Math.atan2(2 * x * w - 2 * y * z, -x2 + y2 - z2 + w2),
Math.atan2(2 * y * 2 - 2 * x * z, x2 - y2 - z2 + w2), Math.asin(2 * test / length));
}
}
private static double fastisqrt(double x) {
double xhalf = 0.5d * x;
long i = Double.doubleToLongBits(x);
i = 0x5fe6ec85e7de30daL - (i >> 1);
x = Double.longBitsToDouble(i);
x *= (1.5d - xhalf * x * x);
return x;
}
public static Quaternion from(EulerAngle from) {
return fromEuler(from.getX(), from.getY(), from.getZ());
}
public static Quaternion fromEuler(double x, double y, double z) {
double c1 = Math.cos(x * 0.5);
double c2 = Math.cos(y * 0.5);
double c3 = Math.cos(z * 0.5);
double s1 = Math.sin(x * 0.5);
double s2 = Math.sin(y * 0.5);
double s3 = Math.sin(z * 0.5);
double c1c2 = c1 * c2;
double s1s2 = s1 * s2;
return new Quaternion(s1s2 * c3 + c1c2 * s3, s1 * c2 * c3 + c1 * s2 * s3, c1 * s2 * c3 - s1 * c2 * s3,
c1c2 * c3 - s1s2 * s3);
}
public static Quaternion fromEuler(EulerAngle angle) {
return fromEuler(angle.getX(), angle.getY(), angle.getZ());
}
public static Quaternion nlerp(Quaternion a, Quaternion b, double t) {
if (a.dot(b) < 0) {
b = b.mul(-1);
}
return new Quaternion(a.x - t * (a.x - b.x), a.y - t * (a.y - b.y), a.z - t * (a.z - b.z),
a.w - t * (a.w - b.w)).norm();
}
public static Quaternion slerp(Quaternion a, Quaternion b, double t) {
double len = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
if (len < 0) {
b = b.mul(-1);
len = -len;
}
double sa = 1 - t;
double sb = t;
if (0.1 < 1 - len) {
double theta = Math.acos(len);
double itheta = 1 / Math.sin(theta);
sa = Math.sin(theta * (1 - t)) * itheta;
sb = Math.sin(theta * t) * itheta;
}
return new Quaternion(sa * a.x + sb * b.x, sa * a.y + sb * b.y, sa * a.z + sb * b.z, sa * a.w + sb * b.w);
}
private static final Quaternion ZERO = new Quaternion(0, 0, 0, 0);
}

View File

@ -333,6 +333,11 @@ public class Util {
return list;
}
public static int parseTicks(String raw) {
Duration duration = SpigotUtil.parseDuration(raw);
return duration == null ? -1 : toTicks(duration);
}
public static String prettyEnum(Enum<?> e) {
return e.name().toLowerCase().replace('_', ' ');
}

View File

@ -66,7 +66,7 @@ citizens.commands.npc.command.left-hand-header=Commands to run on [[left click]]
citizens.commands.npc.command.right-hand-header=Commands to run on [[right click]]:
citizens.commands.npc.command.command-removed=Command [[{0}]] removed.
citizens.commands.npc.command.command-added=Command [[{0}]] added with id [[{1}]].
citizens.commands.npc.command.describe-format=<br> - {0} [{1}s] [<click:run_command:/npc cmd remove {3}><hover:show_text:Remove this command><red>-</hover></click>]
citizens.commands.npc.command.describe-format=<br> - {0} [{1}s] [<click:run_command:/npc cmd remove {3}><hover:show_text:Remove this command><red><u>-</hover></click>]
citizens.commands.npc.command.help=<br>Use the [[-l]] flag to make the command run on left click, [[-r]] on right click (default).<br>Set the per-player cooldown before the command can be used again using [[--cooldown]] (in [[seconds]]).<br>Set the server-wide cooldown in seconds using [[--gcooldown]].<br>[[--delay]] will wait the specified amount in [[ticks]] before executing the command.<br>[[--permissions]] will set the command to require specific permissions (separate multiple with commas).<br>[[--n]] will only let the player run the command that number of times.<br>Use [[-o]] to temporarily execute the command as an op and [[-p]] to run the command as the clicking player instead of the server.<br>To give the player temporary permissions instead of op, use [[/npc command permissions]].<br>Set the cost of each click with [[/npc command cost/expcost/itemcost]].<br>Commands can be executed one by one instead of all at once by using [[/npc command sequential]].
citizens.commands.npc.command.unknown-id=Unknown command id [[{0}]] for this NPC.
citizens.commands.npc.command.temporary-permissions-set=Temporary permissions set to [[{0}]].
@ -140,7 +140,7 @@ citizens.commands.npc.horse.invalid-color=Invalid horse color given. Valid color
citizens.commands.npc.horse.invalid-type=Invalid horse type given. Valid types are: [[{0}]].
citizens.commands.npc.horse.invalid-style=Invalid horse style given. Valid styles are: [[{0}]].
citizens.commands.npc.hurt.not-damageable=NPC type [[{0}]] cannot be hurt.
citizens.commands.npc.hurt.protected=NPC is protected and cannot be hurt. <click:run_command:/npc vulnerable>[[Click to unprotect</click>
citizens.commands.npc.hurt.protected=NPC is protected and cannot be hurt. <click:run_command:/npc vulnerable>[[<u>Click to unprotect</click>
citizens.commands.npc.item.item-set=NPC item set to [[{0}]].
citizens.commands.npc.item.unknown-material=Unknown material given.
citizens.commands.npc.leashable.set=[[{0}]] is now leashable.
@ -258,7 +258,7 @@ citizens.commands.npc.rabbittype.type-set=[[{0}]]''s rabbit type has been set to
citizens.commands.npc.remove.npcs-removed=NPCs removed.
citizens.commands.npc.remove.incorrect-syntax=Incorrect syntax. /npc remove (all)
citizens.commands.npc.remove.removed-all=You permanently removed all NPCs.
citizens.commands.npc.remove.removed=You permanently removed [[{0}]] ID [[{1}]] (<click:run_command:/npc undo><hover:show_text:Undo this removal><red>undo</red></hover></click>).
citizens.commands.npc.remove.removed=You permanently removed [[{0}]] ID [[{1}]] (<click:run_command:/npc undo><hover:show_text:Undo this removal><red><u>undo</u></red></hover></click>).
citizens.commands.npc.rename.renamed=You renamed [[{0}]] to [[{1}]].
citizens.commands.npc.respawn.delay-set=Respawn delay set to [[{0}]] ticks.
citizens.commands.npc.respawn.describe=Respawn delay is currently [[{0}]] ticks.
@ -358,9 +358,9 @@ citizens.economy.loaded=Loaded economy handling via Vault.
citizens.economy.minimum-cost-required=Need at least [[{0}]].
citizens.economy.money-withdrawn=Withdrew [[{0}]] for your NPC.
citizens.editors.already-in-editor=You''re already in an editor!
citizens.editors.copier.begin=<aqua>Entered the NPC copier!<br>Click anywhere to copy the currently selected NPC.
citizens.editors.copier.begin=Entered the NPC copier!<br>Click anywhere to copy the currently selected NPC.
citizens.editors.copier.end=Exited the NPC copier.
citizens.editors.equipment.begin=<aqua>Entered the equipment editor!<br>[[Right click]] to equip the NPC or [[crouch right click]] to change the item in hand!<br>Type [[offhand]], [[chestplate]], [[helmet]], etc. in chat to equip specific slots with the item you''re holding!
citizens.editors.equipment.begin=Entered the equipment editor!<br>[[Right click]] to equip the NPC or [[crouch right click]] to change the item in hand!<br>Type [[offhand]], [[chestplate]], [[helmet]], etc. in chat to equip specific slots with the item you''re holding!
citizens.editors.equipment.end=Exited the equipment editor.
citizens.editors.equipment.saddled-set=[[{0}]] is now saddled.
citizens.editors.equipment.saddled-stopped=[[{0}]] is no longer saddled.
@ -370,7 +370,7 @@ citizens.editors.equipment.sheep-coloured=[[{0}]] is now coloured [[{1}]].
citizens.editors.selection.start-prompt=There were multiple NPCs with the supplied name.<br>Please enter an id or number from the list below to select that NPC.
citizens.editors.text.add-prompt=Enter text to add to the NPC.
citizens.editors.text.added-entry=[[Added]] the entry [[{0}]].
citizens.editors.text.begin=<aqua>Entered the text editor! Type ''exit'' to leave the editor.
citizens.editors.text.begin=Entered the text editor! Type [[exit]] to leave the editor.
citizens.editors.text.change-page-prompt=Enter a page number to view more text entries.
citizens.editors.text.close-talker-set=[[Close talker]] set to [[{0}]].
citizens.editors.text.end=Exited the text editor.
@ -386,14 +386,14 @@ citizens.editors.text.range-set=[[Range]] set to [[{0}]].
citizens.editors.text.delay-set=[[Delay]] set to [[{0}]] ticks.
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 ticks><yellow>delay</yellow></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><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: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.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.
citizens.editors.waypoints.wander.worldguard-region-not-found=WorldGuard region not found.
citizens.editors.waypoints.wander.worldguard-region-set=WorldGuard region set to [[{0}]].
citizens.editors.waypoints.wander.range-set=Wander range set to xrange [[{0}]] and yrange [[{1}]].
citizens.editors.waypoints.wander.begin=<aqua>Entered the wander waypoint editor.<br><click:suggest_command:pathfind><hover:show_text:Pathfind>{0}Pathfind</hover></click> | <click:suggest_command:xrange ><hover:show_text:Set the x range in blocks><yellow>x range</hover></click> | <click:suggest_command:yrange ><hover:show_text:Set the y range in blocks><yellow>y range</hover></click> | <click:suggest_command:delay ><hover:show_text:Ticks to wait in between wanders><yellow>delay</hover></click><br><click:suggest_command:regions><yellow>Enter the region editor</click> | <click:suggest_command:worldguardregion ><hover:show_text:Restrict wandering to WorldGuard regions><yellow>WorldGuard region</hover></click>
citizens.editors.waypoints.wander.begin=Entered the wander waypoint editor.<br><click:suggest_command:pathfind><hover:show_text:Pathfind>{0}<u>Pathfind</hover></click> | <click:suggest_command:xrange ><hover:show_text:Set the x range in blocks><yellow><u>x range</hover></click> | <click:suggest_command:yrange ><hover:show_text:Set the y range in blocks><yellow><u>y range</hover></click> | <click:suggest_command:delay ><hover:show_text:Ticks to wait in between wanders><yellow><u>delay</hover></click><br><click:suggest_command:regions><yellow><u>Enter the region editor</click> | <click:suggest_command:worldguardregion ><hover:show_text:Restrict wandering to WorldGuard regions><yellow><u>WorldGuard region</hover></click>
citizens.editors.waypoints.wander.end=Exited the wander waypoint editor.
citizens.editors.waypoints.wander.delay-set=Delay between wanders set to [[{0}]] ticks.
citizens.editors.waypoints.wander.invalid-delay=Invalid delay specified.
@ -401,12 +401,12 @@ citizens.editors.waypoints.wander.added-region=[[Added]] wanderable region at ({
citizens.editors.waypoints.wander.removed-region=[[Removed]] wanderable region at ({0}) ([[{1}]] remaining).
citizens.editors.waypoints.wander.editing-regions=Now editing regions!<br> [[Left click]] to add a new wanderable region using the xrange/yrange box centred at that block.<br> [[Right click]] an existing marker to remove that region.<br> Type [[regions]] to stop or simply exit the editor. Regions should be overlap or touch each other.
citizens.editors.waypoints.guided.end=Exited the guided waypoint editor.
citizens.editors.waypoints.guided.begin=<aqua>Entered the guided waypoint editor! The NPC will randomly walk to a [[destination]] waypoint following the nearest [[guide]] point. For example, try putting guide points on a road and destinations inside houses!<br> [[Left click]] to add a guide waypoint and [[right click]] to remove an existing waypoint.<br> [[Sneak]] while left clicking to add a destination waypoint.<br> Type [[toggle path]] to toggle showing entities at waypoints.<br> Type [[clear]] to clear all waypoints.
citizens.editors.waypoints.guided.begin=Entered the guided waypoint editor! The NPC will randomly walk to a [[destination]] waypoint following the nearest [[guide]] point. For example, try putting guide points on a road and destinations inside houses!<br> [[Left click]] to add a guide waypoint and [[right click]] to remove an existing waypoint.<br> [[Sneak]] while left clicking to add a destination waypoint.<br> Type [[toggle path]] to toggle showing entities at waypoints.<br> Type [[clear]] to clear all waypoints.
citizens.editors.waypoints.guided.added-guide=Added a [[guide]] waypoint which the NPC will follow on the way to their destination.
citizens.editors.waypoints.guided.added-available=Added a [[destination]] waypoint which the NPC will randomly pathfind between.
citizens.editors.waypoints.guided.already-taken=There is already a waypoint here.
citizens.editors.waypoints.linear.added-waypoint=[[Added]] a waypoint at ({0}) ([[{1}]] total).
citizens.editors.waypoints.linear.begin=<green>=== [[Linear Waypoint Editor]] ===<br> [[Left click]] to add a waypoint, [[right click]] to remove it.<br> Right click while sneaking to select and remove points.<br> Type [[markers]] to hide waypoints,<br> [[triggers]] to enter the trigger editor,<br> [[clear]] to clear all waypoints,<br> [[cycle]] to make NPCs cycle through waypoints instead of looping.
citizens.editors.waypoints.linear.begin=<green>=== [[Linear Waypoint Editor]] ===<br> [[Left click]] to add a waypoint, [[right click]] to remove it.<br> Right click while sneaking to select and remove points.<br> Type [[markers]] to hide waypoints,<br> [[triggers]] to enter the trigger editor,<br> [[clear]] to clear all waypoints,<br> [[cycle]] to cycle waypoints instead of looping.
citizens.editors.waypoints.linear.selected-waypoint=Selected waypoint at {0}. Sneak + right click again to remove this waypoint.
citizens.editors.waypoints.linear.end=Exited the linear waypoint editor.
citizens.editors.waypoints.linear.not-showing-markers=[[Stopped]] showing waypoint markers.
@ -416,7 +416,7 @@ citizens.editors.waypoints.linear.showing-markers=[[Showing]] waypoint markers.
citizens.editors.waypoints.linear.waypoints-cleared=Waypoints cleared.
citizens.editors.waypoints.linear.cycle-set=Now [[cycling]] through waypoints.
citizens.editors.waypoints.linear.cycle-unset=Now [[looping]] through waypoints.
citizens.editors.waypoints.triggers.add.added=<aqua>Added waypoint trigger successfully ({0}).
citizens.editors.waypoints.triggers.add.added=Added waypoint trigger successfully ({0}).
citizens.editors.waypoints.triggers.add.invalid-trigger=Couldn''t create a trigger by the name [[{0}]].
citizens.editors.waypoints.triggers.add.prompt=Enter in a trigger name to add or type [[back]] to return to the edit prompt. Valid trigger names are {0}.
citizens.editors.waypoints.triggers.animation.added=Animation [[{0}]] added.
@ -428,14 +428,10 @@ citizens.editors.waypoints.triggers.chat.missing-radius=No radius supplied.
citizens.editors.waypoints.triggers.chat.message-added=Message added: [[{0}]].
citizens.editors.waypoints.triggers.chat.radius-set=Radius set to [[{0}]] blocks.
citizens.editors.waypoints.triggers.chat.prompt=Enter in chat lines to say.<br>Type in [[radius (radius)]] to set the block radius to broadcast the messages.<br>Type [[finish]] to finish the chat trigger or [[back]] to return to the previous prompt.
citizens.editors.waypoints.triggers.delay.prompt=Enter the delay in [[server ticks]] to use. (20 ticks = 1 second)
citizens.editors.waypoints.triggers.delay.prompt=Enter the delay in [[server ticks]]. You can also specify durations like 1s, 1m or 1hr.
citizens.editors.waypoints.triggers.main.missing-waypoint=Not editing a waypoint.
citizens.editors.waypoints.triggers.main.exit=<aqua>Exited the waypoint trigger editor.
citizens.editors.waypoints.triggers.main.prompt=<aqua>- Waypoint Trigger Editor -<br> Type [[add]] or [[remove]] to edit triggers.<br> Type [[triggers]] or [[exit]] to exit this editor.<br> Current triggers are:{0}
citizens.editors.waypoints.triggers.remove.index-out-of-range=Index must be in the range [[1-{0}]].
citizens.editors.waypoints.triggers.remove.not-a-number=Index must be a number.
citizens.editors.waypoints.triggers.remove.prompt=Enter in the index of the trigger to delete or [[back]] to return to the edit prompt. Current triggers are:{0}
citizens.editors.waypoints.triggers.remove.removed=Successfully removed trigger [[{0}]].
citizens.editors.waypoints.triggers.main.exit=Exited the waypoint trigger editor.
citizens.editors.waypoints.triggers.main.prompt=- Waypoint Trigger Editor -<br> Type [[add]] to add triggers.<br> Type [[triggers]] or [[exit]] to exit this editor.
citizens.editors.waypoints.triggers.speed.prompt=Enter the speed modifier as a [[percentage]] of its base speed.
citizens.editors.waypoints.triggers.teleport.invalid-format=Invalid location given. Format is [[world]]:[[x]]:[[y]]:[[z]].
citizens.editors.waypoints.triggers.teleport.prompt=Enter the destination in the format world:x:y:z. Type [[here]] to use your current location. Type [[back]] to return to the edit prompt.
@ -477,6 +473,6 @@ citizens.settings.writing-default=Writing default setting: {0}
citizens.sub-plugins.error-on-load={0} initializing {1}
citizens.sub-plugins.load=Loading {0}
citizens.traits.age-description={0}''s age is [[{1}]]. Locked is [[{2}]].
citizens.waypoints.available-providers-header=<aqua>List of available providers
citizens.waypoints.available-providers-header=List of available providers
citizens.waypoints.current-provider=The current waypoint provider is [[{0}]].
citizens.waypoints.set-provider=Set the waypoint provider to [[{0}]].

View File

@ -99,8 +99,6 @@ public class HoglinController extends MobEntityController {
}
}
@Override
protected SoundEvent getAmbientSound() {
return NMSImpl.getSoundEffect(npc, super.getAmbientSound(), NPC.Metadata.AMBIENT_SOUND);

View File

@ -51,7 +51,6 @@ public class VillagerController extends MobEntityController {
public static class EntityVillagerNPC extends Villager implements NPCHolder {
private boolean blockingATrade;
private final CitizensNPC npc;
public EntityVillagerNPC(EntityType<? extends Villager> types, Level level) {
@ -107,8 +106,6 @@ public class VillagerController extends MobEntityController {
}
}
@Override
protected SoundEvent getAmbientSound() {
return NMSImpl.getSoundEffect(npc, super.getAmbientSound(), NPC.Metadata.AMBIENT_SOUND);

View File

@ -109,10 +109,9 @@ public class BlockDisplayController extends MobEntityController {
@Override
public void tick() {
super.tick();
if (npc != null) {
npc.update();
} else {
super.tick();
}
}

View File

@ -37,7 +37,9 @@ public class ItemDisplayController extends MobEntityController {
protected org.bukkit.entity.Entity createEntity(Location at, NPC npc) {
final EntityItemDisplayNPC handle = new EntityItemDisplayNPC(EntityType.ITEM_DISPLAY,
((CraftWorld) at.getWorld()).getHandle(), npc);
if (npc != null) {
handle.setItemStack(CraftItemStack.asNMSCopy(npc.getItemProvider().get()));
}
return handle.getBukkitEntity();
}
@ -114,10 +116,9 @@ public class ItemDisplayController extends MobEntityController {
@Override
public void tick() {
super.tick();
if (npc != null) {
npc.update();
} else {
super.tick();
}
}

View File

@ -103,10 +103,9 @@ public class TextDisplayController extends MobEntityController {
@Override
public void tick() {
super.tick();
if (npc != null) {
npc.update();
} else {
super.tick();
}
}

View File

@ -2443,9 +2443,7 @@ public class NMSImpl implements NMSBridge {
private static final MethodHandle ADVANCEMENTS_PLAYER_FIELD = NMS.getFirstFinalSetter(ServerPlayer.class,
PlayerAdvancements.class);
private static final MethodHandle ATTRIBUTE_PROVIDER_MAP = NMS.getFirstGetter(AttributeSupplier.class, Map.class);
private static final MethodHandle ATTRIBUTE_PROVIDER_MAP_SETTER = NMS.getFinalSetter(AttributeSupplier.class, "a");
private static final MethodHandle ATTRIBUTE_SUPPLIER = NMS.getFirstGetter(AttributeMap.class,
AttributeSupplier.class);
@ -2490,7 +2488,7 @@ public class NMSImpl implements NMSBridge {
private static final MethodHandle NAVIGATION_PATHFINDER = NMS.getFirstFinalSetter(PathNavigation.class,
PathFinder.class);
private static final MethodHandle NAVIGATION_WORLD_FIELD = NMS.getFirstSetter(PathNavigation.class, Level.class);
public static final Location PACKET_CACHE_LOCATION = new Location(null, 0, 0, 0);
private static final Location PACKET_CACHE_LOCATION = new Location(null, 0, 0, 0);
private static final MethodHandle PLAYER_CHUNK_MAP_VIEW_DISTANCE_GETTER = NMS.getGetter(ChunkMap.class, "P");
private static final MethodHandle PLAYER_CHUNK_MAP_VIEW_DISTANCE_SETTER = NMS.getSetter(ChunkMap.class, "P");
private static final MethodHandle PLAYER_INFO_ENTRIES_LIST = NMS