diff --git a/src/main/java/net/citizensnpcs/commands/NPCCommands.java b/src/main/java/net/citizensnpcs/commands/NPCCommands.java index 0f25567ed..834d75393 100644 --- a/src/main/java/net/citizensnpcs/commands/NPCCommands.java +++ b/src/main/java/net/citizensnpcs/commands/NPCCommands.java @@ -42,6 +42,7 @@ import net.citizensnpcs.trait.HorseModifiers; import net.citizensnpcs.trait.LookClose; import net.citizensnpcs.trait.NPCSkeletonType; import net.citizensnpcs.trait.OcelotModifiers; +import net.citizensnpcs.trait.PlayerSkin; import net.citizensnpcs.trait.Poses; import net.citizensnpcs.trait.Powered; import net.citizensnpcs.trait.SlimeSize; @@ -446,8 +447,8 @@ public class NPCCommands { public void flyable(CommandContext args, CommandSender sender, NPC npc) throws CommandException { boolean flyable = args.argsLength() == 2 ? args.getString(1).equals("true") : !npc.isFlyable(); npc.setFlyable(flyable); - flyable = npc.isFlyable(); // may not have applied - + flyable = npc.isFlyable(); // may not have applied, eg bats always + // flyable Messaging.sendTr(sender, flyable ? Messages.FLYABLE_SET : Messages.FLYABLE_UNSET); } @@ -952,6 +953,24 @@ public class NPCCommands { npc.getName()); } + @Command( + aliases = { "npc" }, + usage = "skin [skin|-c]", + desc = "Sets a player NPC's skin", + flags = "c", + modifiers = { "skin" }, + min = 1, + max = 2, + permission = "citizens.npc.skin") + @Requirements(selected = true, ownership = true, types = EntityType.PLAYER) + public void playerSkin(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + if (!args.hasFlag('c') && args.argsLength() == 0) + throw new CommandException(); + String skin = args.hasFlag('c') ? "" : args.getString(1); + npc.getTrait(PlayerSkin.class).setSkinName(skin); + Messaging.sendTr(sender, skin.isEmpty() ? Messages.SKIN_CLEARED : Messages.SKIN_SET, npc.getFullName(), skin); + } + @Command( aliases = { "npc" }, usage = "pose (--save [name]|--assume [name]|--remove [name]) (-a)", diff --git a/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java b/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java index df193dfb4..150a595c0 100644 --- a/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java +++ b/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java @@ -26,6 +26,7 @@ import net.citizensnpcs.trait.HorseModifiers; import net.citizensnpcs.trait.LookClose; import net.citizensnpcs.trait.NPCSkeletonType; import net.citizensnpcs.trait.OcelotModifiers; +import net.citizensnpcs.trait.PlayerSkin; import net.citizensnpcs.trait.Poses; import net.citizensnpcs.trait.Powered; import net.citizensnpcs.trait.Saddle; @@ -59,9 +60,9 @@ public class CitizensTraitFactory implements TraitFactory { registerTrait(TraitInfo.create(LookClose.class).withName("lookclose")); registerTrait(TraitInfo.create(OcelotModifiers.class).withName("ocelotmodifiers")); registerTrait(TraitInfo.create(Owner.class).withName("owner")); + registerTrait(TraitInfo.create(PlayerSkin.class).withName("playerskin")); registerTrait(TraitInfo.create(Poses.class).withName("poses")); registerTrait(TraitInfo.create(Powered.class).withName("powered")); - registerTrait(TraitInfo.create(VillagerProfession.class).withName("profession")); registerTrait(TraitInfo.create(Saddle.class).withName("saddle")); registerTrait(TraitInfo.create(Sheared.class).withName("sheared")); registerTrait(TraitInfo.create(NPCSkeletonType.class).withName("skeletontype")); @@ -73,6 +74,7 @@ public class CitizensTraitFactory implements TraitFactory { registerTrait(TraitInfo.create(Waypoints.class).withName("waypoints")); registerTrait(TraitInfo.create(WoolColor.class).withName("woolcolor")); registerTrait(TraitInfo.create(WolfModifiers.class).withName("wolfmodifiers")); + registerTrait(TraitInfo.create(VillagerProfession.class).withName("profession")); registerTrait(TraitInfo.create(ZombieModifier.class).withName("zombiemodifier")); for (String trait : registered.keySet()) { diff --git a/src/main/java/net/citizensnpcs/npc/entity/HumanController.java b/src/main/java/net/citizensnpcs/npc/entity/HumanController.java index 1b226d76d..3ed509f66 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/HumanController.java +++ b/src/main/java/net/citizensnpcs/npc/entity/HumanController.java @@ -7,6 +7,7 @@ import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.util.Colorizer; import net.citizensnpcs.npc.AbstractEntityController; +import net.citizensnpcs.trait.PlayerSkin; import net.citizensnpcs.util.NMS; import net.minecraft.server.v1_7_R2.PlayerInteractManager; import net.minecraft.server.v1_7_R2.WorldServer; @@ -26,9 +27,10 @@ public class HumanController extends AbstractEntityController { @Override protected Entity createEntity(final Location at, final NPC npc) { WorldServer ws = ((CraftWorld) at.getWorld()).getHandle(); - String parseColors = Colorizer.parseColors(npc.getFullName()); - if (parseColors.length() > 16) + String parseColors = Colorizer.parseColors(npc.getTrait(PlayerSkin.class).getSkinName()); + if (parseColors.length() > 16) { parseColors = parseColors.substring(0, 16); + } final EntityHumanNPC handle = new EntityHumanNPC(ws.getServer().getServer(), ws, new GameProfile(UUID .randomUUID().toString(), parseColors), new PlayerInteractManager(ws), npc); handle.setPositionRotation(at.getX(), at.getY(), at.getZ(), at.getYaw(), at.getPitch()); diff --git a/src/main/java/net/citizensnpcs/trait/PlayerSkin.java b/src/main/java/net/citizensnpcs/trait/PlayerSkin.java new file mode 100644 index 000000000..9e2045af7 --- /dev/null +++ b/src/main/java/net/citizensnpcs/trait/PlayerSkin.java @@ -0,0 +1,106 @@ +package net.citizensnpcs.trait; + +import java.util.List; + +import net.citizensnpcs.api.event.DespawnReason; +import net.citizensnpcs.api.npc.NPC; +import net.citizensnpcs.api.trait.Trait; +import net.citizensnpcs.api.trait.trait.MobType; + +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Slime; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import com.google.common.collect.Lists; + +public class PlayerSkin extends Trait { + private final List nameCarriers = Lists.newArrayList(); + + public PlayerSkin() { + super("playerskin"); + } + + private void despawnNameCarriers() { + if (nameCarriers.isEmpty()) + return; + for (Entity entity : nameCarriers) { + entity.remove(); + } + nameCarriers.clear(); + } + + public String getSkinName() { + String skin = npc.data().get(NPC.PLAYER_SKIN_NAME_METADATA, ""); + if (skin.isEmpty()) + skin = npc.getFullName(); + return skin; + } + + public boolean isEnabled() { + return npc.getTrait(MobType.class).getType() == EntityType.PLAYER + && !npc.data().get(NPC.PLAYER_SKIN_NAME_METADATA, "").isEmpty(); + } + + @Override + public void onDespawn() { + despawnNameCarriers(); + } + + @Override + public void onRemove() { + despawnNameCarriers(); + } + + private Entity prepareEntity(String name, EntityType type) { + NPC npcEntity = npc.getOwningRegistry().createNPC(type, name); + npcEntity.data().set(NPC.SHOULD_SAVE_METADATA, false); + npcEntity.spawn(npc.getStoredLocation()); + if (name.isEmpty() || !(npcEntity.getEntity() instanceof Slime)) { + ((LivingEntity) npcEntity.getEntity()).addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, 20 + * 60 * 60 * 24 * 7, 1)); + } else { + ((Slime) npcEntity.getEntity()).setSize(-2); + } + return npcEntity.getEntity(); + } + + private void refreshPlayer() { + despawnNameCarriers(); + + Location last = npc.getEntity().getLocation(); + npc.despawn(DespawnReason.PENDING_RESPAWN); + npc.spawn(last); + } + + @Override + public void run() { + if (!npc.isSpawned() || npc.getEntity().getType() != EntityType.PLAYER + || npc.data().get(NPC.PLAYER_SKIN_NAME_METADATA, "").isEmpty()) { + despawnNameCarriers(); + return; + } + if (nameCarriers.size() == 0) { + Entity previous = npc.getEntity(); + for (int i = 0; i < 2; i++) { + Entity heightCarrier = prepareEntity("", EntityType.SKELETON); + previous.setPassenger(heightCarrier); + nameCarriers.add(previous = heightCarrier); + } + Entity nameCarrier = prepareEntity(npc.getFullName(), EntityType.SLIME); + previous.setPassenger(nameCarrier); + + nameCarriers.add(nameCarrier); + } + } + + public void setSkinName(String name) { + npc.data().setPersistent(NPC.PLAYER_SKIN_NAME_METADATA, name); + if (npc.isSpawned()) { + refreshPlayer(); + } + } +} diff --git a/src/main/java/net/citizensnpcs/util/Messages.java b/src/main/java/net/citizensnpcs/util/Messages.java index a77b0f194..c62984001 100644 --- a/src/main/java/net/citizensnpcs/util/Messages.java +++ b/src/main/java/net/citizensnpcs/util/Messages.java @@ -183,6 +183,8 @@ public class Messages { public static final String SIZE_DESCRIPTION = "citizens.commands.npc.size.description"; public static final String SIZE_SET = "citizens.commands.npc.size.set"; public static final String SKELETON_TYPE_SET = "citizens.commands.npc.skeletontype.set"; + public static final String SKIN_CLEARED = "citizens.commands.npc.skin.cleared"; + public static final String SKIN_SET = "citizens.commands.npc.skin.set"; public static final String SKIPPING_BROKEN_TRAIT = "citizens.notifications.skipping-broken-trait"; public static final String SKIPPING_INVALID_ANCHOR = "citizens.notifications.skipping-invalid-anchor"; public static final String SKIPPING_INVALID_POSE = "citizens.notifications.skipping-invalid-pose"; diff --git a/src/main/java/net/citizensnpcs/util/NMS.java b/src/main/java/net/citizensnpcs/util/NMS.java index 6310d8446..c9cdad850 100644 --- a/src/main/java/net/citizensnpcs/util/NMS.java +++ b/src/main/java/net/citizensnpcs/util/NMS.java @@ -176,7 +176,7 @@ public class NMS { @SuppressWarnings("deprecation") private static Constructor getCustomEntityConstructor(Class clazz, EntityType type) throws SecurityException, - NoSuchMethodException { + NoSuchMethodException { Constructor constructor = ENTITY_CONSTRUCTOR_CACHE.get(clazz); if (constructor == null) { constructor = clazz.getConstructor(World.class); diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties index 04943fb81..77242d947 100644 --- a/src/main/resources/messages_en.properties +++ b/src/main/resources/messages_en.properties @@ -90,6 +90,8 @@ citizens.commands.npc.rename.renamed=You renamed [[{0}]] to [[{1}]]. citizens.commands.npc.respawn.delay-set=Respawn delay set to [[{0}]]. citizens.commands.npc.respawn.describe=Respawn delay is currently [[{0}]]. citizens.commands.npc.select.already-selected=You already have that NPC selected. +citizens.commands.npc.skin.set=[[{0}]]''s skin name set to [[{1}]]. +citizens.commands.npc.skin.cleared=[[{0}]]''s skin name was cleared. citizens.commands.npc.size.description={0}''s size is [[{1}]]. citizens.commands.npc.size.set={0}''s size set to [[{1}]]. citizens.commands.npc.sound.invalid-sound=Invalid sound.