Rework /npc controllable, add /npc shop copyfrom, add /npc entitypose, add /npc forcefield, add /npc display

This commit is contained in:
fullwall 2024-06-06 12:55:29 +08:00
parent c6207e6d99
commit 468703fe79
16 changed files with 449 additions and 194 deletions

View File

@ -93,6 +93,7 @@ import net.citizensnpcs.api.npc.NPC.NPCUpdate;
import net.citizensnpcs.api.npc.NPCRegistry;
import net.citizensnpcs.api.npc.templates.Template;
import net.citizensnpcs.api.npc.templates.TemplateRegistry;
import net.citizensnpcs.api.persistence.PersistenceLoader;
import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.trait.trait.Equipment;
import net.citizensnpcs.api.trait.trait.Equipment.EquipmentSlot;
@ -101,7 +102,9 @@ 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.util.DataKey;
import net.citizensnpcs.api.util.EntityDim;
import net.citizensnpcs.api.util.MemoryDataKey;
import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.api.util.Paginator;
import net.citizensnpcs.api.util.Placeholders;
@ -126,11 +129,15 @@ import net.citizensnpcs.trait.CommandTrait.ExecutionMode;
import net.citizensnpcs.trait.CommandTrait.ItemRequirementGUI;
import net.citizensnpcs.trait.CommandTrait.NPCCommandBuilder;
import net.citizensnpcs.trait.Controllable;
import net.citizensnpcs.trait.Controllable.BuiltInControls;
import net.citizensnpcs.trait.CurrentLocation;
import net.citizensnpcs.trait.DropsTrait;
import net.citizensnpcs.trait.EnderCrystalTrait;
import net.citizensnpcs.trait.EndermanTrait;
import net.citizensnpcs.trait.EntityPoseTrait;
import net.citizensnpcs.trait.EntityPoseTrait.EntityPose;
import net.citizensnpcs.trait.FollowTrait;
import net.citizensnpcs.trait.ForcefieldTrait;
import net.citizensnpcs.trait.GameModeTrait;
import net.citizensnpcs.trait.Gravity;
import net.citizensnpcs.trait.HologramTrait;
@ -434,13 +441,11 @@ public class NPCCommands {
public void boat(CommandContext args, CommandSender sender, NPC npc,
@Flag(value = "type", completionsProvider = OptionalBoatTypeCompletions.class) String stype)
throws CommandException {
if (stype != null) {
Boat.Type type = Boat.Type.valueOf(stype);
npc.getOrAddTrait(BoatTrait.class).setType(type);
Messaging.sendTr(sender, Messages.BOAT_TYPE_SET, type);
return;
}
throw new CommandUsageException();
if (stype == null)
throw new CommandUsageException();
Boat.Type type = Boat.Type.valueOf(stype);
npc.getOrAddTrait(BoatTrait.class).setType(type);
Messaging.sendTr(sender, Messages.BOAT_TYPE_SET, type);
}
@Command(
@ -452,7 +457,7 @@ public class NPCCommands {
max = 1,
valueFlags = "location",
permission = "citizens.npc.breakblock")
@Requirements(selected = true, ownership = true, livingEntity = true)
@Requirements(selected = true, ownership = true)
public void breakblock(CommandContext args, CommandSender sender, NPC npc, @Flag("radius") Double radius)
throws CommandException {
BlockBreakerConfiguration cfg = new BlockBreakerConfiguration();
@ -694,31 +699,34 @@ public class NPCCommands {
@Command(
aliases = { "npc" },
usage = "controllable|control (-m(ount),-y,-n,-o(wner required))",
usage = "controllable|control (-m(ount),-o(wner required)) (--controls [controls]) (--enabled [true|false])",
desc = "",
modifiers = { "controllable", "control" },
min = 1,
max = 1,
flags = "myno")
public void controllable(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
flags = "mo")
public void controllable(CommandContext args, CommandSender sender, NPC npc,
@Flag("controls") BuiltInControls controls, @Flag("enabled") Boolean enabled) throws CommandException {
if ((npc.isSpawned() && !sender.hasPermission(
"citizens.npc.controllable." + npc.getEntity().getType().name().toLowerCase().replace("_", "")))
|| !sender.hasPermission("citizens.npc.controllable"))
throw new NoPermissionsException();
if (!npc.hasTrait(Controllable.class)) {
if (!npc.hasTrait(Controllable.class) && enabled == null) {
npc.getOrAddTrait(Controllable.class).setEnabled(false);
}
Controllable trait = npc.getOrAddTrait(Controllable.class);
boolean enabled = trait.toggle();
if (args.hasFlag('y')) {
enabled = trait.setEnabled(true);
} else if (args.hasFlag('n')) {
enabled = trait.setEnabled(false);
if (enabled != null) {
trait.setEnabled(enabled);
} else {
enabled = trait.toggle();
}
if (controls != null) {
trait.setControls(controls);
}
trait.setOwnerRequired(args.hasFlag('o'));
String key = enabled ? Messages.CONTROLLABLE_SET : Messages.CONTROLLABLE_REMOVED;
Messaging.sendTr(sender, key, npc.getName());
if (enabled && args.hasFlag('m') && sender instanceof Player) {
if (trait.isEnabled() && args.hasFlag('m') && sender instanceof Player) {
trait.mount((Player) sender);
}
}
@ -1045,6 +1053,22 @@ public class NPCCommands {
throw new CommandUsageException();
}
@Command(
aliases = { "npc" },
usage = "entitypose [pose]",
desc = "",
modifiers = { "entitypose" },
min = 2,
max = 2,
permission = "citizens.npc.entitypose")
public void entitypose(CommandContext args, CommandSender sender, NPC npc, @Arg(1) EntityPose pose)
throws CommandException {
if (pose == null)
throw new CommandUsageException();
npc.getOrAddTrait(EntityPoseTrait.class).setPose(pose);
Messaging.sendTr(sender, Messages.ENTITYPOSE_SET, pose);
}
@Command(
aliases = { "npc" },
usage = "flyable (true|false)",
@ -1112,6 +1136,38 @@ public class NPCCommands {
player.getName());
}
@Command(
aliases = { "npc" },
usage = "forcefield --width [width] --height [height] --strength [strength]",
desc = "",
modifiers = { "forcefield" },
min = 1,
max = 1,
permission = "citizens.npc.forcefield")
public void forcefield(CommandContext args, CommandSender sender, NPC npc, @Flag("width") Double width,
@Flag("height") Double height, @Flag("strength") Double strength) throws CommandException {
ForcefieldTrait trait = npc.getOrAddTrait(ForcefieldTrait.class);
String output = "";
if (width != null) {
trait.setWidth(width);
output += Messaging.tr(Messages.FORCEFIELD_WIDTH_SET, width);
}
if (height != null) {
trait.setHeight(height);
output += Messaging.tr(Messages.FORCEFIELD_HEIGHT_SET, height);
}
if (strength != null) {
trait.setStrength(strength);
output += Messaging.tr(Messages.FORCEFIELD_STRENGTH_SET, strength);
}
if (!output.isEmpty()) {
Messaging.send(sender, output);
} else {
Messaging.sendTr(sender, Messages.FORCEFIELD_DESCRIBE, npc.getName(), trait.getHeight(), trait.getWidth(),
trait.getStrength());
}
}
@Command(
aliases = { "npc" },
usage = "gamemode [gamemode]",
@ -2374,8 +2430,6 @@ public class NPCCommands {
}
npc.data().setPersistent(NPC.Metadata.REMOVE_FROM_PLAYERLIST, remove);
if (npc.isSpawned()) {
npc.despawn(DespawnReason.PENDING_RESPAWN);
npc.spawn(npc.getOrAddTrait(CurrentLocation.class).getLocation(), SpawnReason.RESPAWN);
NMS.addOrRemoveFromPlayerList(npc.getEntity(), remove);
}
Messaging.sendTr(sender, remove ? Messages.REMOVED_FROM_PLAYERLIST : Messages.ADDED_TO_PLAYERLIST,
@ -2805,7 +2859,7 @@ public class NPCCommands {
@Command(
aliases = { "npc" },
usage = "shop (edit|show|delete) (name)",
usage = "shop (edit|show|delete|copyfrom) (name)",
desc = "",
modifiers = { "shop" },
min = 1,
@ -2813,7 +2867,8 @@ public class NPCCommands {
permission = "citizens.npc.shop")
@Requirements(selected = false, ownership = true)
public void shop(CommandContext args, Player sender, NPC npc,
@Arg(value = 1, completions = { "edit", "show", "delete" }) String action) throws CommandException {
@Arg(value = 1, completions = { "edit", "show", "delete", "copyfrom" }) String action)
throws CommandException {
if (args.argsLength() == 1) {
if (npc != null) {
npc.getOrAddTrait(ShopTrait.class).getDefaultShop().display(sender);
@ -2836,6 +2891,13 @@ public class NPCCommands {
if (!shop.canEdit(npc, sender))
throw new NoPermissionsException();
shop.displayEditor(npc == null ? null : npc.getOrAddTrait(ShopTrait.class), sender);
} else if (action.equalsIgnoreCase("copyfrom")) {
if (!shop.canEdit(npc, sender) || !npc.getOrAddTrait(ShopTrait.class).getDefaultShop().canEdit(npc, sender))
throw new NoPermissionsException();
DataKey key = new MemoryDataKey();
PersistenceLoader.save(shop, key);
NPCShop copy = PersistenceLoader.load(NPCShop.class, key);
npc.getOrAddTrait(ShopTrait.class).setDefaultShop(copy);
} else if (action.equalsIgnoreCase("show")) {
shop.display(sender);
} else
@ -3235,9 +3297,9 @@ public class NPCCommands {
permission = "citizens.npc.swim")
public void swim(CommandContext args, CommandSender sender, NPC npc, @Flag("set") Boolean set)
throws CommandException {
boolean swim = set != null ? set : !npc.data().get(NPC.Metadata.SWIMMING, true);
npc.data().setPersistent(NPC.Metadata.SWIMMING, swim);
Messaging.sendTr(sender, swim ? Messages.SWIMMING_SET : Messages.SWIMMING_UNSET, npc.getName());
boolean swim = set != null ? set : !npc.data().get(NPC.Metadata.SWIM, true);
npc.data().setPersistent(NPC.Metadata.SWIM, swim);
Messaging.sendTr(sender, swim ? Messages.SWIM_SET : Messages.SWIM_UNSET, npc.getName());
}
@Command(
@ -3276,12 +3338,24 @@ public class NPCCommands {
flags = "t",
permission = "citizens.npc.targetable")
public void targetable(CommandContext args, CommandSender sender, NPC npc) {
boolean targetable = !npc.data().get(NPC.Metadata.TARGETABLE, !npc.isProtected());
boolean targetable = !npc.data().get(NPC.Metadata.TARGETABLE, npc.isProtected());
if (args.hasFlag('t')) {
npc.data().set(NPC.Metadata.TARGETABLE, targetable);
} else {
npc.data().setPersistent(NPC.Metadata.TARGETABLE, targetable);
}
if (targetable && npc.getOrAddTrait(MobType.class).getType() == EntityType.PLAYER
&& npc.data().get(NPC.Metadata.REMOVE_FROM_PLAYERLIST, true)) {
Messaging.sendTr(sender, Messages.TARGETABLE_PLAYERLIST_WARNING);
if (args.hasFlag('t')) {
npc.data().set(NPC.Metadata.REMOVE_FROM_PLAYERLIST, false);
} else {
npc.data().setPersistent(NPC.Metadata.REMOVE_FROM_PLAYERLIST, false);
}
if (npc.isSpawned()) {
NMS.addOrRemoveFromPlayerList(npc.getEntity(), false);
}
}
Messaging.sendTr(sender, targetable ? Messages.TARGETABLE_SET : Messages.TARGETABLE_UNSET, npc.getName());
}

View File

@ -11,11 +11,9 @@ import com.google.common.collect.Lists;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.command.Command;
import net.citizensnpcs.api.command.CommandConfigurable;
import net.citizensnpcs.api.command.CommandContext;
import net.citizensnpcs.api.command.Requirements;
import net.citizensnpcs.api.command.exception.CommandException;
import net.citizensnpcs.api.command.exception.NoPermissionsException;
import net.citizensnpcs.api.event.NPCTraitCommandAttachEvent;
import net.citizensnpcs.api.event.NPCTraitCommandDetachEvent;
import net.citizensnpcs.api.npc.NPC;
@ -67,30 +65,6 @@ public class TraitCommands {
Bukkit.getPluginManager().callEvent(new NPCTraitCommandAttachEvent(npc, clazz, sender));
}
@Command(
aliases = { "traitc", "trc" },
usage = "[trait name] (flags)",
desc = "",
modifiers = { "*" },
min = 1,
flags = "*",
permission = "citizens.npc.trait-configure")
public void configure(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
String traitName = args.getString(0);
if (!sender.hasPermission("citizens.npc.trait-configure." + traitName)
&& !sender.hasPermission("citizens.npc.trait-configure.*"))
throw new NoPermissionsException();
Class<? extends Trait> clazz = CitizensAPI.getTraitFactory().getTraitClass(args.getString(0));
if (clazz == null)
throw new CommandException(Messages.TRAIT_NOT_FOUND);
if (!CommandConfigurable.class.isAssignableFrom(clazz))
throw new CommandException(Messages.TRAIT_NOT_CONFIGURABLE);
if (!npc.hasTrait(clazz))
throw new CommandException(Messages.TRAIT_NOT_FOUND_ON_NPC);
CommandConfigurable trait = (CommandConfigurable) npc.getOrAddTrait(clazz);
trait.configure(args);
}
@Command(
aliases = { "trait" },
usage = "remove [trait names]...",

View File

@ -486,7 +486,7 @@ public class CitizensNPC extends AbstractNPC {
NMS.activate(getEntity());
}
}
boolean shouldSwim = data().get(NPC.Metadata.SWIMMING, SwimmingExaminer.isWaterMob(getEntity()))
boolean shouldSwim = data().get(NPC.Metadata.SWIM, SwimmingExaminer.isWaterMob(getEntity()))
&& MinecraftBlockExaminer.isLiquid(getEntity().getLocation().getBlock().getType());
if (navigator.isNavigating()) {
if (shouldSwim) {

View File

@ -34,6 +34,7 @@ import net.citizensnpcs.trait.EnderCrystalTrait;
import net.citizensnpcs.trait.EndermanTrait;
import net.citizensnpcs.trait.EntityPoseTrait;
import net.citizensnpcs.trait.FollowTrait;
import net.citizensnpcs.trait.ForcefieldTrait;
import net.citizensnpcs.trait.GameModeTrait;
import net.citizensnpcs.trait.Gravity;
import net.citizensnpcs.trait.HologramTrait;
@ -90,6 +91,7 @@ public class CitizensTraitFactory implements TraitFactory {
registerTrait(TraitInfo.create(EntityPoseTrait.class));
registerTrait(TraitInfo.create(Equipment.class));
registerTrait(TraitInfo.create(FollowTrait.class).optInToStats());
registerTrait(TraitInfo.create(ForcefieldTrait.class).optInToStats());
registerTrait(TraitInfo.create(GameModeTrait.class));
registerTrait(TraitInfo.create(Gravity.class));
registerTrait(TraitInfo.create(HomeTrait.class).optInToStats());

View File

@ -45,9 +45,10 @@ public class Anchors extends Trait {
}
public Anchor getAnchor(String name) {
for (Anchor anchor : anchors)
for (Anchor anchor : anchors) {
if (anchor.getName().equalsIgnoreCase(name))
return anchor;
}
return null;
}
@ -75,11 +76,7 @@ public class Anchors extends Trait {
}
public boolean removeAnchor(Anchor anchor) {
if (anchors.contains(anchor)) {
anchors.remove(anchor);
return true;
}
return false;
return anchors.remove(anchor);
}
@Override

View File

@ -1,13 +1,10 @@
package net.citizensnpcs.trait;
import java.lang.reflect.Constructor;
import java.util.List;
import java.util.Map;
import org.bukkit.Location;
import org.bukkit.entity.EnderDragon;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Vehicle;
@ -17,19 +14,13 @@ import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.util.Vector;
import com.google.common.collect.Maps;
import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.command.CommandConfigurable;
import net.citizensnpcs.api.command.CommandContext;
import net.citizensnpcs.api.event.NPCRightClickEvent;
import net.citizensnpcs.api.exception.NPCLoadException;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.persistence.Persist;
import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.trait.TraitName;
import net.citizensnpcs.api.trait.trait.Owner;
import net.citizensnpcs.api.util.DataKey;
import net.citizensnpcs.util.NMS;
import net.citizensnpcs.util.Util;
@ -40,11 +31,12 @@ import net.citizensnpcs.util.Util;
* e.g. arrow keys.
*/
@TraitName("controllable")
public class Controllable extends Trait implements Toggleable, CommandConfigurable {
public class Controllable extends Trait implements Toggleable {
private MovementController controller = new GroundController();
@Persist
private BuiltInControls controls;
@Persist
private boolean enabled = true;
private EntityType explicitType;
@Persist("owner_required")
private boolean ownerRequired;
@ -52,29 +44,6 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
super("controllable");
}
/**
* Configures the explicit type parameter.
*
* @see #setExplicitType(EntityType)
*/
@Override
public void configure(CommandContext args) {
if (args.hasFlag('f')) {
explicitType = EntityType.BLAZE;
} else if (args.hasFlag('g')) {
explicitType = EntityType.OCELOT;
} else if (args.hasFlag('o')) {
explicitType = EntityType.UNKNOWN;
} else if (args.hasFlag('r')) {
explicitType = null;
} else if (args.hasValueFlag("explicittype")) {
explicitType = Util.matchEnum(EntityType.values(), args.getFlag("explicittype"));
}
if (npc.isSpawned()) {
loadController();
}
}
private void enterOrLeaveVehicle(Player player) {
List<Entity> passengers = NMS.getPassengers(player);
if (passengers.size() > 0) {
@ -96,36 +65,18 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
return enabled;
}
@Override
public void load(DataKey key) throws NPCLoadException {
if (key.keyExists("explicittype")) {
explicitType = Util.matchEnum(EntityType.values(), key.getString("explicittype"));
}
}
private void loadController() {
EntityType type = npc.getEntity().getType();
if (explicitType != null) {
type = explicitType;
if (controls != null) {
controller = controls.create(this);
return;
}
if (!(npc.getEntity() instanceof LivingEntity) && !(npc.getEntity() instanceof Vehicle) && (explicitType == null
|| explicitType == EntityType.UNKNOWN || npc.getEntity().getType() == explicitType)) {
if (!(npc.getEntity() instanceof LivingEntity) && !(npc.getEntity() instanceof Vehicle)) {
controller = new LookAirController();
return;
}
Constructor<? extends MovementController> innerConstructor = CONTROLLER_TYPES.get(type);
if (innerConstructor == null) {
controller = new GroundController();
return;
}
try {
if (innerConstructor.getParameterCount() == 0) {
controller = innerConstructor.newInstance();
} else {
controller = innerConstructor.newInstance(this);
}
} catch (Exception e) {
e.printStackTrace();
if (Util.isAlwaysFlyable(npc.getEntity().getType())) {
controller = new PlayerInputAirController();
} else {
controller = new GroundController();
}
}
@ -202,13 +153,8 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
controller.run((Player) passengers.get(0));
}
@Override
public void save(DataKey key) {
if (explicitType == null) {
key.removeKey("explicittype");
} else {
key.setString("explicittype", explicitType.name());
}
public void setControls(BuiltInControls controls) {
this.controls = controls;
}
public boolean setEnabled(boolean enabled) {
@ -216,16 +162,6 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
return enabled;
}
/**
* Configures the explicit typei.e. whether the NPC should be controlled as if it was a certain {@link EntityType}.
*
* @param type
* the explicit type
*/
public void setExplicitType(EntityType type) {
explicitType = type;
}
private void setMountedYaw(Entity entity) {
if (entity instanceof EnderDragon || !Setting.USE_BOAT_CONTROLS.asBoolean())
return; // EnderDragon handles this separately
@ -297,6 +233,35 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
}
}
public enum BuiltInControls {
AIR {
@Override
MovementController create(Controllable trait) {
return trait.new PlayerInputAirController();
}
},
GROUND {
@Override
MovementController create(Controllable trait) {
return trait.new GroundController();
}
},
GROUND_JUMPLESS {
@Override
MovementController create(Controllable trait) {
return trait.new JumplessGroundController();
}
},
LOOK_AIR {
@Override
MovementController create(Controllable trait) {
return trait.new LookAirController();
}
};
abstract MovementController create(Controllable trait);
}
public class GroundController implements MovementController {
private int jumpTicks = 0;
private double speed = 0.07D;
@ -336,6 +301,38 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
private static final float JUMP_VELOCITY = 0.5F;
}
public class JumplessGroundController implements MovementController {
private double speed = 0.07D;
@Override
public void leftClick(PlayerInteractEvent event) {
}
@Override
public void rightClick(PlayerInteractEvent event) {
}
@Override
public void rightClickEntity(NPCRightClickEvent event) {
enterOrLeaveVehicle(event.getClicker());
}
@Override
public void run(Player rider) {
boolean onGround = NMS.isOnGround(npc.getEntity());
float impulse = npc.getNavigator().getDefaultParameters()
.modifiedSpeed(onGround ? GROUND_SPEED : AIR_SPEED);
if (!Util.isHorse(npc.getEntity().getType())) {
speed = updateHorizontalSpeed(npc.getEntity(), rider, speed, impulse,
Setting.MAX_CONTROLLABLE_GROUND_SPEED.asDouble());
}
setMountedYaw(npc.getEntity());
}
private static final float AIR_SPEED = 0.5F;
private static final float GROUND_SPEED = 0.5F;
}
public class LookAirController implements MovementController {
private boolean paused = false;
@ -412,44 +409,4 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
setMountedYaw(npc.getEntity());
}
}
/**
* Register a movement controller for a certain {@link EntityType} to be used for {@link NPC}s with that type.
*
* Default controllers are registered for BAT, BLAZE, ENDER_DRAGON, GHAST, WITHER and PARROT using
* {@link PlayerInputAirController}.
*
* @param type
* the entity type
* @param clazz
* the controller class
*/
public static void registerControllerType(EntityType type, Class<? extends MovementController> clazz) {
try {
Constructor<? extends MovementController> constructor = clazz.getConstructor(Controllable.class);
constructor.setAccessible(true);
CONTROLLER_TYPES.put(type, constructor);
return;
} catch (Exception e) {
try {
Constructor<? extends MovementController> constructor = clazz.getConstructor();
constructor.setAccessible(true);
CONTROLLER_TYPES.put(type, constructor);
} catch (Exception e2) {
throw new RuntimeException(e2);
}
}
}
private static Map<EntityType, Constructor<? extends MovementController>> CONTROLLER_TYPES = Maps
.newEnumMap(EntityType.class);
static {
for (EntityType type : EntityType.values()) {
if (Util.isAlwaysFlyable(type)) {
registerControllerType(type, PlayerInputAirController.class);
}
}
registerControllerType(EntityType.UNKNOWN, LookAirController.class);
}
}

View File

@ -0,0 +1,68 @@
package net.citizensnpcs.trait;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.persistence.Persist;
import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.trait.TraitName;
@TraitName("forcefieldtrait")
public class ForcefieldTrait extends Trait {
@Persist
private Double height;
@Persist
private Double strength;
@Persist
private Double width;
public ForcefieldTrait() {
super("forcefieldtrait");
}
public double getHeight() {
return height == null ? npc.getEntity().getHeight() : height;
}
public double getStrength() {
return strength == null ? 0.1 : strength;
}
public double getWidth() {
return width == null ? npc.getEntity().getWidth() : width;
}
@Override
public void run() {
if (!npc.isSpawned())
return;
double height = getHeight();
double width = getWidth();
double strength = getStrength();
Location base = npc.getEntity().getLocation();
for (Player player : CitizensAPI.getLocationLookup().getNearbyVisiblePlayers(npc.getEntity(),
new double[] { base.getX() - width / 1.9, base.getY(), base.getZ() - width / 1.9 },
new double[] { base.getX() + width / 1.9, base.getY() + height, base.getZ() + width / 1.9 })) {
Vector diff = player.getLocation().subtract(base).toVector();
if (diff.isZero())
continue;
diff = diff.normalize().setY(0);
Vector force = player.getVelocity().add(diff.multiply(strength));
player.setVelocity(force);
}
}
public void setHeight(Double height) {
this.height = height;
}
public void setStrength(Double strength) {
this.strength = strength;
}
public void setWidth(Double width) {
this.width = width;
}
}

View File

@ -457,12 +457,11 @@ public class HologramTrait extends Trait {
mb = 0.21;
mt = 0.07;
hr = new ItemRenderer();
} else if (SUPPORTS_DISPLAY && backgroundColor != null) {
renderer = new TextDisplayRenderer(backgroundColor);
}
this.persist = persist;
this.ticks = ticks;
this.renderer = hr;
renderer.setBackgroundColor(backgroundColor);
if (renderer instanceof SingleEntityHologramRenderer) {
SingleEntityHologramRenderer sr = (SingleEntityHologramRenderer) renderer;
sr.setViewRange(viewRange);

View File

@ -117,6 +117,10 @@ public class ShopTrait extends Trait {
shop.display(player);
}
public void setDefaultShop(NPCShop shop) {
shops.npcShops.put(npc.getUniqueId().toString(), shop);
}
public static class NPCShop {
@Persist(value = "")
private String name;

View File

@ -0,0 +1,175 @@
package net.citizensnpcs.trait.versioned;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Display;
import org.bukkit.entity.Display.Billboard;
import org.bukkit.entity.Display.Brightness;
import org.bukkit.entity.EntityType;
import org.bukkit.util.Transformation;
import org.bukkit.util.Vector;
import org.joml.Quaternionf;
import net.citizensnpcs.api.command.Command;
import net.citizensnpcs.api.command.CommandContext;
import net.citizensnpcs.api.command.Flag;
import net.citizensnpcs.api.command.Requirements;
import net.citizensnpcs.api.command.exception.CommandException;
import net.citizensnpcs.api.command.exception.CommandUsageException;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.persistence.Persist;
import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.trait.TraitName;
import net.citizensnpcs.api.util.Messaging;
@TraitName("displaytrait")
public class DisplayTrait extends Trait {
@Persist
private Billboard billboard;
@Persist
private Integer blockLight;
@Persist
private Float height;
@Persist
private Integer interpolationDelay;
@Persist
private Integer interpolationDuration;
@Persist
private Quaternionf leftRotation;
@Persist
private Quaternionf rightRotation;
@Persist
private Vector scale;
@Persist
private Integer skyLight;
@Persist
private Float viewRange;
@Persist
private Float width;
public DisplayTrait() {
super("displaytrait");
}
@Override
public void onSpawn() {
Display display = (Display) npc.getEntity();
if (billboard != null) {
display.setBillboard(billboard);
}
if (blockLight != null && skyLight != null) {
display.setBrightness(new Brightness(blockLight, skyLight));
}
if (interpolationDelay != null) {
display.setInterpolationDelay(interpolationDelay);
}
if (interpolationDuration != null) {
display.setInterpolationDuration(interpolationDuration);
}
if (height != null) {
display.setDisplayHeight(height);
}
if (width != null) {
display.setDisplayWidth(width);
}
Transformation tf = display.getTransformation();
if (scale != null) {
tf.getScale().set(scale.getX(), scale.getY(), scale.getZ());
}
if (leftRotation != null) {
tf.getLeftRotation().set(leftRotation);
}
if (rightRotation != null) {
tf.getRightRotation().set(rightRotation);
}
display.setTransformation(tf);
if (viewRange != null) {
display.setViewRange(viewRange);
}
}
public void setBillboard(Billboard billboard) {
this.billboard = billboard;
}
public void setBrightness(Brightness brightness) {
this.blockLight = brightness.getBlockLight();
this.skyLight = brightness.getSkyLight();
}
public void setHeight(Float height) {
this.height = height;
}
public void setInterpolationDelay(Integer interpolationDelay) {
this.interpolationDelay = interpolationDelay;
}
public void setInterpolationDuration(Integer interpolationDuration) {
this.interpolationDuration = interpolationDuration;
}
public void setScale(Vector scale) {
this.scale = scale;
}
public void setViewRange(Float viewRange) {
this.viewRange = viewRange;
}
public void setWidth(Float width) {
this.width = width;
}
@Command(
aliases = { "npc" },
usage = "display --billboard [billboard] --brightness [blockLight,skyLight] --interpolationdelay [delay] --interpolationduration [duration] --height [height] --width [width] --scale [x,y,z] --viewrange [range] --leftrotation [x,y,z,w] --rightrotation [x,y,z,w]",
desc = "",
modifiers = { "display" },
min = 1,
max = 1,
permission = "citizens.npc.display")
@Requirements(
selected = true,
ownership = true,
types = { EntityType.ITEM_DISPLAY, EntityType.TEXT_DISPLAY, EntityType.BLOCK_DISPLAY })
public static void display(CommandContext args, CommandSender sender, NPC npc,
@Flag("billboard") Billboard billboard, @Flag("leftrotation") Quaternionf leftrotation,
@Flag("rightrotation") Quaternionf rightrotation, @Flag("scale") Vector scale,
@Flag("viewrange") Float viewRange, @Flag("brightness") String brightness,
@Flag("interpolationdelay") Integer interpolationDelay,
@Flag("interpolationduration") Integer interpolationDuration, @Flag("height") Float height,
@Flag("width") Float width) throws CommandException {
DisplayTrait trait = npc.getOrAddTrait(DisplayTrait.class);
String output = "";
if (billboard != null) {
trait.setBillboard(billboard);
}
if (brightness != null) {
trait.setBrightness(new Brightness(Integer.parseInt(brightness.split(",")[0]),
Integer.parseInt(brightness.split(",")[1])));
}
if (interpolationDelay != null) {
trait.setInterpolationDelay(interpolationDelay);
}
if (interpolationDuration != null) {
trait.setInterpolationDuration(interpolationDuration);
}
if (width != null) {
trait.setWidth(width);
}
if (height != null) {
trait.setHeight(height);
}
if (viewRange != null) {
trait.setViewRange(viewRange);
}
if (scale != null) {
trait.setScale(scale);
}
trait.onSpawn();
if (!output.isEmpty()) {
Messaging.send(sender, output.trim());
} else
throw new CommandUsageException();
}
}

View File

@ -103,6 +103,7 @@ public class Messages {
public static final String ENDERMAN_ANGRY_SET = "citizens.commands.npc.enderman.angry-set";
public static final String ENDERMAN_ANGRY_UNSET = "citizens.commands.npc.enderman.angry-unset";
public static final String ENTITY_TYPE_SET = "citizens.commands.npc.type.set";
public static final String ENTITYPOSE_SET = "citizens.commands.npc.entitypose.set";
public static final String EQUIPMENT_EDITOR_BEGIN = "citizens.editors.equipment.begin";
public static final String EQUIPMENT_EDITOR_END = "citizens.editors.equipment.end";
public static final String EQUIPMENT_EDITOR_SHEEP_COLOURED = "citizens.editors.equipment.sheep-coloured";
@ -126,6 +127,10 @@ public class Messages {
public static final String FOLLOW_PLAYER_NOT_INGAME = "citizens.commands.npc.follow.player-not-ingame";
public static final String FOLLOW_SET = "citizens.commands.npc.follow.set";
public static final String FOLLOW_UNSET = "citizens.commands.npc.follow.unset";
public static final String FORCEFIELD_DESCRIBE = "citizens.commands.npc.forcefield.describe";
public static final String FORCEFIELD_HEIGHT_SET = "citizens.commands.npc.forcefield.height-set";
public static final String FORCEFIELD_STRENGTH_SET = "citizens.commands.npc.forcefield.strength-set";
public static final String FORCEFIELD_WIDTH_SET = "citizens.commands.npc.forcefield.width-set";
public static final String FOX_CROUCHING_SET = "citizens.commands.npc.fox.crouching-set";
public static final String FOX_CROUCHING_UNSET = "citizens.commands.npc.fox.crouching-unset";
public static final String FOX_FACEPLANTED_SET = "citizens.commands.npc.fox.faceplanted-set";
@ -407,8 +412,9 @@ public class Messages {
public static final String SPEED_MODIFIER_SET = "citizens.commands.npc.speed.set";
public static final String SPEED_TRIGGER_PROMPT = "citizens.editors.waypoints.triggers.speed.prompt";
public static final String SPELL_SET = "citizens.commands.npc.spellcaster.spell-set";
public static final String SWIMMING_SET = "citizens.commands.npc.swim.set";
public static final String SWIMMING_UNSET = "citizens.commands.npc.swim.unset";
public static final String SWIM_SET = "citizens.commands.npc.swim.set";
public static final String SWIM_UNSET = "citizens.commands.npc.swim.unset";
public static final String TARGETABLE_PLAYERLIST_WARNING = "citizens.commands.npc.targetable.playerlist-warning";
public static final String TARGETABLE_SET = "citizens.commands.npc.targetable.set";
public static final String TARGETABLE_UNSET = "citizens.commands.npc.targetable.unset";
public static final String TELEPORT_NPC_LOCATION_NOT_FOUND = "citizens.commands.npc.tp.location-not-found";
@ -442,9 +448,6 @@ public class Messages {
public static final String TPTO_SUCCESS = "citizens.commands.npc.tpto.success";
public static final String TRACKING_RANGE_SET = "citizens.commands.npc.trackingrange.set";
public static final String TRAIT_LOAD_FAILED = "citizens.notifications.trait-load-failed";
public static final String TRAIT_NOT_CONFIGURABLE = "citizens.commands.traitc.not-configurable";
public static final String TRAIT_NOT_FOUND = "citizens.commands.traitc.missing";
public static final String TRAIT_NOT_FOUND_ON_NPC = "citizens.commands.traitc.not-on-npc";
public static final String TRAIT_ONSPAWN_FAILED = "citizens.notifications.trait-onspawn-failed";
public static final String TRAITS_ADDED = "citizens.commands.trait.added";
public static final String TRAITS_FAILED_TO_ADD = "citizens.commands.trait.failed-to-add";

View File

@ -13,6 +13,7 @@
"citizens.commands.help.header" : "Help",
"citizens.commands.id-not-found" : "Couldn''t find any NPC with ID [[{0}]].",
"citizens.commands.invalid-mobtype" : "[[{0}]] is not a valid mobtype.",
"citizens.commands.npc.targetable.playerlist-warning" : "Adding NPC to the player list to allow targeting by other mobs. This may cause plugin conflicts. You can turn this off by using /npc playerlist at any time.",
"citizens.commands.invalid-number" : "That is not a valid number.",
"citizens.commands.invalid.class" : "Invalid external commands class.",
"citizens.commands.npc.activationrange.description" : "Sets the activation range",
@ -23,13 +24,21 @@
"citizens.commands.npc.age.help" : "Can only be used on entities that can become babies. Use the [[-l]] flag to lock age over time (note: relogs may be required to see this).",
"citizens.commands.npc.age.invalid-age" : "Invalid age. Valid ages are adult, baby, number between -24000 and 0",
"citizens.commands.npc.age.locked" : "Age locked.",
"citizens.commands.npc.forcefield.describe": "[[{0}] has a forcefield with height [[{1}]], width [[{2}]], strength [[{3}]].",
"citizens.commands.npc.forcefield.description": "Creates a forcefield which pushes players close to the NPC away",
"citizens.commands.npc.age.set" : "[[{0}]] is now [[{1}]].",
"citizens.commands.npc.forcefield.width-set": "Forcefield width set to [[{0}]] blocks.",
"citizens.commands.npc.forcefield.height-set": "Forcefield height set to [[{0}]] blocks.",
"citizens.commands.npc.forcefield.strength-set": "Forcefield strength set to [[{0}]] blocks per tick.",
"citizens.commands.npc.age.set-adult" : "[[{0}]] is now an adult.",
"citizens.commands.npc.age.set-baby" : "[[{0}]] is now a baby.",
"citizens.commands.npc.display.description" : "Set various display entity modifiers",
"citizens.commands.npc.age.set-normal" : "[[{0}]] is now age [[{1}]].",
"citizens.commands.npc.age.unlocked" : "Age unlocked.",
"citizens.commands.npc.aggressive.description" : "Sets the aggressive status of the entity",
"citizens.commands.npc.aggressive.help" : "",
"citizens.commands.npc.entitypose.set" : "Set entity pose to [[{0}]]",
"citizens.commands.npc.entitypose.description" : "Control entity pose",
"citizens.commands.npc.ai.description" : "Sets whether the NPC should use vanilla AI",
"citizens.commands.npc.attribute.set" : "Attribute [[{0}]] set to base value [[{1}]].",
"citizens.commands.npc.attribute.reset" : "Attribute [[{0}]] reset to default value.",
@ -641,12 +650,7 @@
"citizens.commands.trait.failed-to-remove" : "Couldn''t remove {0}.",
"citizens.commands.trait.remove.description" : "Removes traits on the NPC",
"citizens.commands.trait.remove.help" : "",
"citizens.commands.trait.removed" : "Removed {0} successfully.",
"citizens.commands.traitc.*.description" : "Configures a trait",
"citizens.commands.traitc.*.help" : "",
"citizens.commands.traitc.missing" : "Trait not found.",
"citizens.commands.traitc.not-configurable" : "That trait is not configurable.",
"citizens.commands.traitc.not-on-npc" : "The NPC doesn''t have that trait.",
"citizens.commands.trait.removed" : "Removed {0} successfully.",
"citizens.commands.unknown-command" : "Unknown command.",
"citizens.commands.waypoints.add.description" : "Adds a waypoint at a point",
"citizens.commands.waypoints.add.help" : "",

View File

@ -6,10 +6,6 @@ main: net.citizensnpcs.Citizens
website: https://www.citizensnpcs.co
api-version: "1.13"
commands:
traitc:
aliases: [trc]
description: Configures traits
permission: citizens.trait.help
trait:
description: Trait commands
permission: citizens.trait.help

View File

@ -223,6 +223,7 @@ import net.citizensnpcs.trait.versioned.BossBarTrait;
import net.citizensnpcs.trait.versioned.CamelTrait;
import net.citizensnpcs.trait.versioned.CamelTrait.CamelPose;
import net.citizensnpcs.trait.versioned.CatTrait;
import net.citizensnpcs.trait.versioned.DisplayTrait;
import net.citizensnpcs.trait.versioned.EnderDragonTrait;
import net.citizensnpcs.trait.versioned.FoxTrait;
import net.citizensnpcs.trait.versioned.FrogTrait;
@ -958,6 +959,7 @@ public class NMSImpl implements NMSBridge {
registerTraitWithCommand(manager, BossBarTrait.class);
registerTraitWithCommand(manager, CamelTrait.class);
registerTraitWithCommand(manager, CatTrait.class);
registerTraitWithCommand(manager, DisplayTrait.class);
registerTraitWithCommand(manager, FoxTrait.class);
registerTraitWithCommand(manager, FrogTrait.class);
registerTraitWithCommand(manager, GoatTrait.class);

View File

@ -54,9 +54,7 @@ public class HorseController extends MobEntityController {
public static class EntityHorseNPC extends Horse implements NPCHolder {
private double baseMovementSpeed;
private final CitizensNPC npc;
private boolean riding;
public EntityHorseNPC(EntityType<? extends Horse> types, Level level) {

View File

@ -221,6 +221,7 @@ import net.citizensnpcs.trait.versioned.BossBarTrait;
import net.citizensnpcs.trait.versioned.CamelTrait;
import net.citizensnpcs.trait.versioned.CamelTrait.CamelPose;
import net.citizensnpcs.trait.versioned.CatTrait;
import net.citizensnpcs.trait.versioned.DisplayTrait;
import net.citizensnpcs.trait.versioned.EnderDragonTrait;
import net.citizensnpcs.trait.versioned.FoxTrait;
import net.citizensnpcs.trait.versioned.FrogTrait;
@ -935,6 +936,7 @@ public class NMSImpl implements NMSBridge {
registerTraitWithCommand(manager, BossBarTrait.class);
registerTraitWithCommand(manager, CamelTrait.class);
registerTraitWithCommand(manager, CatTrait.class);
registerTraitWithCommand(manager, DisplayTrait.class);
registerTraitWithCommand(manager, FoxTrait.class);
registerTraitWithCommand(manager, FrogTrait.class);
registerTraitWithCommand(manager, GoatTrait.class);