Add /npc command cycle

This commit is contained in:
fullwall 2024-02-05 00:05:51 +08:00
parent deded77f34
commit 66708ac314
10 changed files with 64 additions and 56 deletions

View File

@ -455,7 +455,7 @@ public class NPCCommands {
@Command(
aliases = { "npc" },
usage = "command|cmd (add [command] | remove [id|all] | permissions [permissions] | sequential | random | clearerror [type] (name|uuid) | errormsg [type] [msg] | persistsequence [true|false] | cost [cost] (id) | expcost [cost] (id) | itemcost (id)) (-s(hift)) (-l[eft]/-r[ight]) (-p[layer] -o[p]), --cooldown --gcooldown [seconds] --delay [ticks] --permissions [perms] --n [max # of uses]",
usage = "command|cmd (add [command] | remove [id|all] | permissions [permissions] | sequential | cycle | random | clearerror [type] (name|uuid) | errormsg [type] [msg] | persistsequence [true|false] | cost [cost] (id) | expcost [cost] (id) | itemcost (id)) (-s(hift)) (-l[eft]/-r[ight]) (-p[layer] -o[p]), --cooldown --gcooldown [seconds] --delay [ticks] --permissions [perms] --n [max # of uses]",
desc = "Controls commands which will be run when clicking on an NPC",
help = Messages.NPC_COMMAND_HELP,
modifiers = { "command", "cmd" },
@ -469,7 +469,7 @@ public class NPCCommands {
@Flag(value = "delay", defValue = "0") Duration delay,
@Arg(
value = 1,
completions = { "add", "remove", "permissions", "persistsequence", "sequential", "random",
completions = { "add", "remove", "permissions", "persistsequence", "sequential", "cycle", "random",
"hideerrors", "errormsg", "clearerror", "expcost", "itemcost", "cost" }) String action)
throws CommandException {
CommandTrait commands = npc.getOrAddTrait(CommandTrait.class);
@ -521,6 +521,11 @@ public class NPCCommands {
Messaging.sendTr(sender,
commands.getExecutionMode() == ExecutionMode.SEQUENTIAL ? Messages.COMMANDS_SEQUENTIAL_SET
: Messages.COMMANDS_SEQUENTIAL_UNSET);
} else if (action.equalsIgnoreCase("cycle")) {
commands.setExecutionMode(
commands.getExecutionMode() == ExecutionMode.CYCLE ? ExecutionMode.LINEAR : ExecutionMode.CYCLE);
Messaging.sendTr(sender, commands.getExecutionMode() == ExecutionMode.CYCLE ? Messages.COMMANDS_CYCLE_SET
: Messages.COMMANDS_CYCLE_UNSET);
} else if (action.equalsIgnoreCase("persistsequence")) {
if (args.argsLength() == 2) {
commands.setPersistSequence(!commands.persistSequence());
@ -2078,11 +2083,12 @@ public class NPCCommands {
@Command(
aliases = { "npc" },
usage = "pathto me | here | cursor | [x] [y] [z] (--margin [distance margin])",
usage = "pathto me | here | cursor | [x] [y] [z] (--margin [distance margin]) (-s[traight line])",
desc = "Starts pathfinding to a certain location",
modifiers = { "pathto" },
min = 2,
max = 4,
flags = "s",
permission = "citizens.npc.pathto")
public void pathto(CommandContext args, CommandSender sender, NPC npc,
@Arg(value = 1, completions = { "me", "here", "cursor" }) String option, @Flag("margin") Double margin)
@ -2100,7 +2106,11 @@ public class NPCCommands {
loc.setY(args.getDouble(2));
loc.setZ(args.getDouble(3));
}
npc.getNavigator().setTarget(loc);
if (args.hasFlag('s')) {
npc.getNavigator().setStraightLineTarget(loc);
} else {
npc.getNavigator().setTarget(loc);
}
if (margin != null) {
npc.getNavigator().getLocalParameters().distanceMargin(margin);
}

View File

@ -191,9 +191,7 @@ public class CitizensNPCRegistry implements NPCRegistry {
public NPC getNPC(Entity entity) {
if (entity == null)
return null;
if (entity instanceof NPCHolder)
return ((NPCHolder) entity).getNPC();
return NMS.getNPC(entity);
return entity instanceof NPCHolder ? ((NPCHolder) entity).getNPC() : NMS.getNPC(entity);
}
@Override

View File

@ -98,7 +98,7 @@ public class AStarNavigationStrategy extends AbstractPathStrategy {
}
Location loc = npc.getEntity().getLocation();
/* 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();
@ -112,8 +112,8 @@ public class AStarNavigationStrategy extends AbstractPathStrategy {
double dX = dest.getX() - loc.getX();
double dZ = dest.getZ() - loc.getZ();
double dY = dest.getY() - loc.getY();
double xzDistance = dX * dX + dZ * dZ;
if (Math.abs(dY) < 1 && Math.sqrt(xzDistance) <= params.distanceMargin()) {
double xzDistance = Math.sqrt(dX * dX + dZ * dZ);
if (Math.abs(dY) < 1 && xzDistance <= params.distanceMargin()) {
plan.update(npc);
if (plan.isComplete())
return true;
@ -128,7 +128,7 @@ public class AStarNavigationStrategy extends AbstractPathStrategy {
} else {
Vector dir = dest.toVector().subtract(npc.getEntity().getLocation().toVector()).normalize().multiply(0.2);
boolean liquidOrInLiquid = MinecraftBlockExaminer.isLiquidOrInLiquid(loc.getBlock());
if (dY >= 1 && Math.sqrt(xzDistance) <= 0.4 || dY >= 0.2 && liquidOrInLiquid) {
if (dY >= 1 && xzDistance <= 0.4 || dY >= 0.2 && liquidOrInLiquid) {
dir.add(new Vector(0, 0.75, 0));
}
npc.getEntity().setVelocity(dir);

View File

@ -334,12 +334,13 @@ public class CitizensNavigator implements Navigator, Runnable {
return;
}
setTarget(params -> {
if (npc.isFlyable())
if (npc.isFlyable()) {
return new FlyingAStarNavigationStrategy(npc, path, params);
else if (params.useNewPathfinder() || !(npc.getEntity() instanceof LivingEntity))
} else if (params.useNewPathfinder() || !(npc.getEntity() instanceof LivingEntity)) {
return new AStarNavigationStrategy(npc, path, params);
else
} else {
return new MCNavigationStrategy(npc, path, params);
}
});
}
@ -353,12 +354,13 @@ public class CitizensNavigator implements Navigator, Runnable {
}
Location target = targetIn.clone();
setTarget(params -> {
if (npc.isFlyable())
if (npc.isFlyable()) {
return new FlyingAStarNavigationStrategy(npc, target, params);
else if (params.useNewPathfinder() || !(npc.getEntity() instanceof LivingEntity))
} else if (params.useNewPathfinder() || !(npc.getEntity() instanceof LivingEntity)) {
return new AStarNavigationStrategy(npc, target, params);
else
} else {
return new MCNavigationStrategy(npc, target, params);
}
});
}

View File

@ -62,9 +62,12 @@ public class StraightLineNavigationStrategy extends AbstractPathStrategy {
return true;
Location currLoc = npc.getEntity().getLocation();
if (currLoc.distance(destination) <= params.distanceMargin())
if (currLoc.distance(destination) <= params.distanceMargin()) {
if (npc.isFlyable()) {
npc.getEntity().setVelocity(new Vector(0, 0, 0));
}
return true;
}
if (target != null) {
destination = params.entityTargetLocationMapper().apply(target);
}

View File

@ -77,6 +77,7 @@ public class CommandTrait extends Trait {
private boolean hideErrorMessages;
@Persist
private final List<ItemStack> itemRequirements = Lists.newArrayList();
private int lastUsedId = -1;
@Persist
private boolean persistSequence = false;
@Persist(keyType = UUID.class, reify = true, value = "cooldowns")
@ -278,7 +279,7 @@ public class CommandTrait extends Trait {
return;
}
int max = -1;
if (executionMode == ExecutionMode.SEQUENTIAL) {
if (executionMode == ExecutionMode.SEQUENTIAL || executionMode == ExecutionMode.CYCLE) {
Collections.sort(commandList, Comparator.comparing(o1 -> o1.id));
max = commandList.size() > 0 ? commandList.get(commandList.size() - 1).id : -1;
}
@ -287,6 +288,13 @@ public class CommandTrait extends Trait {
}
for (NPCCommand command : commandList) {
PlayerNPCCommand info = null;
if (executionMode == ExecutionMode.CYCLE) {
if (command.id <= lastUsedId) {
if (lastUsedId != max)
continue;
lastUsedId = -1;
}
}
if (executionMode == ExecutionMode.SEQUENTIAL
&& (info = playerTracking.get(player.getUniqueId())) != null) {
if (info.lastUsedHand != hand) {
@ -300,7 +308,8 @@ public class CommandTrait extends Trait {
}
}
runCommand(player, hand, command);
if (executionMode == ExecutionMode.SEQUENTIAL || (charged != null && !charged))
if (executionMode == ExecutionMode.SEQUENTIAL || executionMode == ExecutionMode.CYCLE
|| (charged != null && !charged))
break;
}
}
@ -508,6 +517,7 @@ public class CommandTrait extends Trait {
}
public enum ExecutionMode {
CYCLE,
LINEAR,
RANDOM,
SEQUENTIAL;

View File

@ -31,7 +31,6 @@ import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.gui.CitizensInventoryClickEvent;
import net.citizensnpcs.api.gui.ClickHandler;
import net.citizensnpcs.api.gui.InputMenus;
import net.citizensnpcs.api.gui.InputMenus.Choice;
import net.citizensnpcs.api.gui.InventoryMenu;
import net.citizensnpcs.api.gui.InventoryMenuPage;
import net.citizensnpcs.api.gui.InventoryMenuPattern;
@ -114,8 +113,6 @@ public class ShopTrait extends Trait {
@Persist
private String title;
@Persist
private ShopType type = ShopType.COMMAND;
@Persist
private String viewPermission;
private NPCShop() {
@ -712,42 +709,31 @@ public class ShopTrait extends Trait {
@Override
public void initialise(MenuContext ctx) {
this.ctx = ctx;
ctx.getSlot(2)
ctx.getSlot(0)
.setDescription("<f>Edit permission required to view shop<br>" + shop.getRequiredPermission());
ctx.getSlot(6).setDescription("<f>Edit shop title<br>" + shop.title);
ctx.getSlot(4).setDescription("<f>Edit shop title<br>" + shop.title);
if (trait != null) {
ctx.getSlot(8).setDescription(
ctx.getSlot(6).setDescription(
"<f>Show shop on right click<br>" + shop.getName().equals(trait.rightClickShop));
}
}
@MenuSlot(slot = { 0, 4 }, material = Material.FEATHER, amount = 1, title = "<f>Edit shop items")
@MenuSlot(slot = { 0, 2 }, material = Material.FEATHER, amount = 1, title = "<f>Edit shop items")
public void onEditItems(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
ctx.getMenu().transition(new NPCShopContentsEditor(shop));
}
@MenuSlot(slot = { 0, 2 }, compatMaterial = { "OAK_SIGN", "SIGN" }, amount = 1)
@MenuSlot(slot = { 0, 0 }, compatMaterial = { "OAK_SIGN", "SIGN" }, amount = 1)
public void onPermissionChange(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
ctx.getMenu().transition(InputMenus.stringSetter(shop::getRequiredPermission, shop::setPermission));
}
@MenuSlot(slot = { 0, 6 }, material = Material.NAME_TAG, amount = 1)
@MenuSlot(slot = { 0, 4 }, material = Material.NAME_TAG, amount = 1)
public void onSetTitle(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
ctx.getMenu().transition(InputMenus.stringSetter(() -> shop.title, newTitle -> shop.title = newTitle));
}
@MenuSlot(slot = { 0, 0 }, material = Material.BOOK, amount = 1, title = "<f>Edit shop type")
public void onShopTypeChange(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
ctx.getMenu().transition(InputMenus.<ShopType> picker("Edit shop type",
chosen -> shop.type = chosen.getValue(),
Choice.<ShopType> of(ShopType.BUY, Material.DIAMOND, "Players buy items",
shop.type == ShopType.BUY),
Choice.of(ShopType.SELL, Material.EMERALD, "Players sell items", shop.type == ShopType.SELL),
Choice.of(ShopType.COMMAND, Util.getFallbackMaterial("ENDER_EYE", "ENDER_PEARL"),
"Clicks trigger commands only", shop.type == ShopType.COMMAND)));
}
@MenuSlot(slot = { 0, 8 }, compatMaterial = { "COMMAND_BLOCK", "COMMAND" }, amount = 1)
@MenuSlot(slot = { 0, 6 }, compatMaterial = { "COMMAND_BLOCK", "COMMAND" }, amount = 1)
public void onToggleRightClick(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
event.setCancelled(true);
if (trait == null)
@ -758,7 +744,7 @@ public class ShopTrait extends Trait {
} else {
trait.rightClickShop = shop.name;
}
ctx.getSlot(8)
ctx.getSlot(6)
.setDescription("<f>Show shop on right click<br>" + shop.getName().equals(trait.rightClickShop));
}
}
@ -830,12 +816,6 @@ public class ShopTrait extends Trait {
}
}
public enum ShopType {
BUY,
COMMAND,
SELL;
}
static {
NPCShopAction.register(ItemAction.class, "items", new ItemActionGUI());
NPCShopAction.register(PermissionAction.class, "permissions", new PermissionActionGUI());

View File

@ -271,14 +271,15 @@ public class GuidedWaypointProvider implements EnumerableWaypointProvider {
tree.clear();
treePlusDestinations.clear();
for (Waypoint waypoint : guides) {
tree.put(new long[] { waypoint.getLocation().getBlockX(), waypoint.getLocation().getBlockY(),
waypoint.getLocation().getBlockZ() }, waypoint);
treePlusDestinations.put(new long[] { waypoint.getLocation().getBlockX(),
waypoint.getLocation().getBlockY(), waypoint.getLocation().getBlockZ() }, waypoint);
Location location = waypoint.getLocation();
tree.put(new long[] { location.getBlockX(), location.getBlockY(), location.getBlockZ() }, waypoint);
treePlusDestinations.put(new long[] { location.getBlockX(), location.getBlockY(), location.getBlockZ() },
waypoint);
}
for (Waypoint waypoint : destinations) {
treePlusDestinations.put(new long[] { waypoint.getLocation().getBlockX(),
waypoint.getLocation().getBlockY(), waypoint.getLocation().getBlockZ() }, waypoint);
Location location = waypoint.getLocation();
treePlusDestinations.put(new long[] { location.getBlockX(), location.getBlockY(), location.getBlockZ() },
waypoint);
}
if (currentGoal != null) {
currentGoal.onProviderChanged();

View File

@ -79,6 +79,8 @@ public class Messages {
public static final String COMMAND_TRIGGER_PROMPT = "citizens.editors.waypoints.triggers.command.prompt";
public static final String COMMAND_UNKNOWN_COMMAND_ID = "citizens.commands.npc.command.unknown-id";
public static final String COMMANDS_CLEARED = "citizens.commands.npc.command.cleared";
public static final String COMMANDS_CYCLE_SET = "citizens.commands.npc.command.cycle-set";
public static final String COMMANDS_CYCLE_UNSET = "citizens.commands.npc.command.cycle-unset";
public static final String COMMANDS_PERSIST_SEQUENCE_SET = "citizens.commands.npc.command.persist-sequence-set";
public static final String COMMANDS_PERSIST_SEQUENCE_UNSET = "citizens.commands.npc.command.persist-sequence-unset";
public static final String COMMANDS_RANDOM_SET = "citizens.commands.npc.commands.random-set";

View File

@ -52,6 +52,8 @@
"citizens.commands.npc.chunkload.unset" : "[[{0}]] will no longer force chunks to be loaded.",
"citizens.commands.npc.collidable.set" : "[[{0}]] will now collide with entities.",
"citizens.commands.npc.collidable.unset" : "[[{0}]] will no longer collide with entities.",
"citizens.commands.npc.command.cycle-set": "[[{0}]] will now cycle through commands on player click",
"citizens.commands.npc.command.cycle-unset": "[[{0}]] will no longer cycle through commands on player click",
"citizens.commands.npc.command.command-added" : "Command [[{0}]] added with id [[{1}]].",
"citizens.commands.npc.command.command-removed" : "Command [[{0}]] removed.",
"citizens.commands.npc.command.cleared" : "[[{0}]]''s commands cleared.",
@ -60,7 +62,7 @@
"citizens.commands.npc.command.describe-format" : "<br> - {0} [{1}s] [cost:{2}] [exp:{3}] [<click:run_command:/npc cmd remove {4}><hover:show_text:Remove this command><red><u>-</hover></click>]",
"citizens.commands.npc.command.errors-cleared" : "Errors cleared for [[{0}]].",
"citizens.commands.npc.command.experience-cost-set" : "Set xp level cost per click to [[{0}]].",
"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.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]] or [[/npc command cycle]].",
"citizens.commands.npc.command.hide-error-messages-set" : "Now hiding error messages.",
"citizens.commands.npc.command.hide-error-messages-unset" : "No longer hiding error messages.",
"citizens.commands.npc.command.individual-cost-set" : "Set cost per click to [[{0}]] for command id [[{1}]].",