diff --git a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java index 869429967..b114a2da8 100644 --- a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java +++ b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java @@ -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()); } diff --git a/main/src/main/java/net/citizensnpcs/commands/TraitCommands.java b/main/src/main/java/net/citizensnpcs/commands/TraitCommands.java index 7dccd4154..0345eb074 100644 --- a/main/src/main/java/net/citizensnpcs/commands/TraitCommands.java +++ b/main/src/main/java/net/citizensnpcs/commands/TraitCommands.java @@ -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 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]...", diff --git a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java index 2aaf60223..cc967cd15 100644 --- a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java +++ b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java @@ -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) { diff --git a/main/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java b/main/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java index 4ca5fb15a..c608adff2 100644 --- a/main/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java +++ b/main/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java @@ -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()); diff --git a/main/src/main/java/net/citizensnpcs/trait/Anchors.java b/main/src/main/java/net/citizensnpcs/trait/Anchors.java index 5031a154a..7424c1562 100644 --- a/main/src/main/java/net/citizensnpcs/trait/Anchors.java +++ b/main/src/main/java/net/citizensnpcs/trait/Anchors.java @@ -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 diff --git a/main/src/main/java/net/citizensnpcs/trait/Controllable.java b/main/src/main/java/net/citizensnpcs/trait/Controllable.java index acad9f47b..c3d77d87a 100644 --- a/main/src/main/java/net/citizensnpcs/trait/Controllable.java +++ b/main/src/main/java/net/citizensnpcs/trait/Controllable.java @@ -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 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 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 clazz) { - try { - Constructor constructor = clazz.getConstructor(Controllable.class); - constructor.setAccessible(true); - CONTROLLER_TYPES.put(type, constructor); - return; - } catch (Exception e) { - try { - Constructor constructor = clazz.getConstructor(); - constructor.setAccessible(true); - CONTROLLER_TYPES.put(type, constructor); - } catch (Exception e2) { - throw new RuntimeException(e2); - } - } - } - - private static Map> 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); - } } diff --git a/main/src/main/java/net/citizensnpcs/trait/ForcefieldTrait.java b/main/src/main/java/net/citizensnpcs/trait/ForcefieldTrait.java new file mode 100644 index 000000000..59e035cd7 --- /dev/null +++ b/main/src/main/java/net/citizensnpcs/trait/ForcefieldTrait.java @@ -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; + } +} diff --git a/main/src/main/java/net/citizensnpcs/trait/HologramTrait.java b/main/src/main/java/net/citizensnpcs/trait/HologramTrait.java index 97e823326..b0f81a4c3 100644 --- a/main/src/main/java/net/citizensnpcs/trait/HologramTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/HologramTrait.java @@ -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); diff --git a/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java b/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java index 4962fa3a5..d18637b44 100644 --- a/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java @@ -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; diff --git a/main/src/main/java/net/citizensnpcs/trait/versioned/DisplayTrait.java b/main/src/main/java/net/citizensnpcs/trait/versioned/DisplayTrait.java new file mode 100644 index 000000000..874191f14 --- /dev/null +++ b/main/src/main/java/net/citizensnpcs/trait/versioned/DisplayTrait.java @@ -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(); + } +} diff --git a/main/src/main/java/net/citizensnpcs/util/Messages.java b/main/src/main/java/net/citizensnpcs/util/Messages.java index 952a34060..4bb725229 100644 --- a/main/src/main/java/net/citizensnpcs/util/Messages.java +++ b/main/src/main/java/net/citizensnpcs/util/Messages.java @@ -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"; diff --git a/main/src/main/resources/en.json b/main/src/main/resources/en.json index 046003a03..ff9ebedc9 100644 --- a/main/src/main/resources/en.json +++ b/main/src/main/resources/en.json @@ -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" : "", diff --git a/main/src/main/resources/plugin.yml b/main/src/main/resources/plugin.yml index 4075d6795..9d5775b09 100644 --- a/main/src/main/resources/plugin.yml +++ b/main/src/main/resources/plugin.yml @@ -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 diff --git a/v1_19_R3/src/main/java/net/citizensnpcs/nms/v1_19_R3/util/NMSImpl.java b/v1_19_R3/src/main/java/net/citizensnpcs/nms/v1_19_R3/util/NMSImpl.java index 311908850..8f660bb93 100644 --- a/v1_19_R3/src/main/java/net/citizensnpcs/nms/v1_19_R3/util/NMSImpl.java +++ b/v1_19_R3/src/main/java/net/citizensnpcs/nms/v1_19_R3/util/NMSImpl.java @@ -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); diff --git a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/entity/HorseController.java b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/entity/HorseController.java index 7f12097b7..72d233768 100644 --- a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/entity/HorseController.java +++ b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/entity/HorseController.java @@ -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 types, Level level) { diff --git a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java index ffc3d1a1d..35069ade2 100644 --- a/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java +++ b/v1_20_R4/src/main/java/net/citizensnpcs/nms/v1_20_R4/util/NMSImpl.java @@ -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);