diff --git a/dist/pom.xml b/dist/pom.xml
index 50b36e422..561ccad8d 100644
--- a/dist/pom.xml
+++ b/dist/pom.xml
@@ -5,7 +5,7 @@
net.citizensnpcs
citizens-parent
- 2.0.25-SNAPSHOT
+ 2.0.26-SNAPSHOT
citizens
pom
@@ -87,5 +87,12 @@
jar
compile
+
+ ${project.groupId}
+ citizens-v1_15_R1
+ ${project.version}
+ jar
+ compile
+
diff --git a/main/pom.xml b/main/pom.xml
index 5f50b3921..ea279a832 100644
--- a/main/pom.xml
+++ b/main/pom.xml
@@ -7,13 +7,13 @@
net.citizensnpcs
citizens-parent
- 2.0.25-SNAPSHOT
+ 2.0.26-SNAPSHOT
citizens-main
UTF-8
- 1.14.3-R0.1-SNAPSHOT
+ 1.15-R0.1-SNAPSHOT
${project.version}
1.4.12
1.5
diff --git a/main/src/main/java/net/citizensnpcs/util/Messages.java b/main/src/main/java/net/citizensnpcs/util/Messages.java
index 1352db9b3..4e206e0ad 100644
--- a/main/src/main/java/net/citizensnpcs/util/Messages.java
+++ b/main/src/main/java/net/citizensnpcs/util/Messages.java
@@ -18,6 +18,11 @@ public class Messages {
public static final String ANIMATION_ADDED = "citizens.editors.waypoints.triggers.animation.added";
public static final String ANIMATION_TRIGGER_PROMPT = "citizens.editors.waypoints.triggers.animation.prompt";
public static final String AVAILABLE_WAYPOINT_PROVIDERS = "citizens.waypoints.available-providers-header";
+ public static final String BEE_ANGER_SET = "citizens.commands.npc.bee.anger-set";
+ public static final String BEE_HAS_NECTAR = "citizens.commands.npc.bee.has-nectar";
+ public static final String BEE_NO_NECTAR = "citizens.commands.npc.bee.no-nectar";
+ public static final String BEE_NOT_STUNG = "citizens.commands.npc.bee.not-stung";
+ public static final String BEE_STUNG = "citizens.commands.npc.bee.bee-stung";
public static final String BEHAVIOUR_HELP = "citizens.commands.npc.behaviour.help";
public static final String BEHAVIOURS_ADDED = "citizens.commands.npc.behaviour.added";
public static final String BEHAVIOURS_REMOVED = "citizens.commands.npc.behaviour.removed";
@@ -129,6 +134,7 @@ public class Messages {
public static final String INVALID_AGE = "citizens.commands.npc.age.invalid-age";
public static final String INVALID_ANCHOR_NAME = "citizens.commands.npc.anchor.invalid-name";
public static final String INVALID_ANIMATION = "citizens.editors.waypoints.triggers.animation.invalid-animation";
+ public static final String INVALID_BEE_ANGER = "citizens.commands.npc.bee.invalid-anger";
public static final String INVALID_CAT_COLLAR_COLOR = "citizens.commands.npc.cat.invalid-collar-color";
public static final String INVALID_CAT_TYPE = "citizens.commands.npc.cat.invalid-type";
public static final String INVALID_ENTITY_TYPE = "citizens.commands.npc.type.invalid";
diff --git a/main/src/main/resources/messages_en.properties b/main/src/main/resources/messages_en.properties
index 69b5b1b9f..b821fca1c 100644
--- a/main/src/main/resources/messages_en.properties
+++ b/main/src/main/resources/messages_en.properties
@@ -22,6 +22,12 @@ citizens.commands.npc.anchor.already-exists=The anchor [[{0}]] already exists.
citizens.commands.npc.anchor.invalid-name=Invalid anchor name.
citizens.commands.npc.anchor.missing=The anchor [[{1}]] does not exist.
citizens.commands.npc.anchor.removed=Anchor removed.
+citizens.commands.npc.bee.anger-set=Anger set to [[{0}]].
+citizens.commands.npc.bee.has-nectar=[[{0}]] has nectar.
+citizens.commands.npc.bee.no-nectar=[[{0}]] has no nectar.
+citizens.commands.npc.bee.not-stung=[[{0}]] has no longer stung.
+citizens.commands.npc.bee.bee-stung=[[{0}]] has now stung.
+citizens.commands.npc.bee.invalid-anger=Anger should be above zero.
citizens.commands.npc.behaviour.added=Behaviours added.
citizens.commands.npc.behaviour.help=The scripts argument is a comma-separated list of file names. Scripts will be loaded automatically and run every tick. Use the [[-r]] flag to remove behaviours.
citizens.commands.npc.behaviour.removed=Behaviours removed.
diff --git a/pom.xml b/pom.xml
index ea5f5b8ab..28e618922 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,10 +7,10 @@
pom
net.citizensnpcs
citizens-parent
- 2.0.25-SNAPSHOT
+ 2.0.26-SNAPSHOT
Unknown
- 2.0.25
+ 2.0.26
clean package install
@@ -22,7 +22,8 @@
v1_11_R1
v1_12_R1
v1_13_R2
- v1_14_R1
+ v1_14_R1
+ v1_15_R1
dist
\ No newline at end of file
diff --git a/v1_10_R1/pom.xml b/v1_10_R1/pom.xml
index 15c96b3e6..7f0c215bf 100644
--- a/v1_10_R1/pom.xml
+++ b/v1_10_R1/pom.xml
@@ -6,7 +6,7 @@
net.citizensnpcs
citizens-parent
- 2.0.25-SNAPSHOT
+ 2.0.26-SNAPSHOT
citizens-v1_10_R1
diff --git a/v1_11_R1/pom.xml b/v1_11_R1/pom.xml
index bc4810fa2..6ea1d94d6 100644
--- a/v1_11_R1/pom.xml
+++ b/v1_11_R1/pom.xml
@@ -6,7 +6,7 @@
net.citizensnpcs
citizens-parent
- 2.0.25-SNAPSHOT
+ 2.0.26-SNAPSHOT
citizens-v1_11_R1
diff --git a/v1_12_R1/pom.xml b/v1_12_R1/pom.xml
index 355626a71..b5795ad3e 100644
--- a/v1_12_R1/pom.xml
+++ b/v1_12_R1/pom.xml
@@ -6,7 +6,7 @@
net.citizensnpcs
citizens-parent
- 2.0.25-SNAPSHOT
+ 2.0.26-SNAPSHOT
citizens-v1_12_R1
diff --git a/v1_13_R2/pom.xml b/v1_13_R2/pom.xml
index 991636b29..8dff87d7a 100644
--- a/v1_13_R2/pom.xml
+++ b/v1_13_R2/pom.xml
@@ -6,7 +6,7 @@
net.citizensnpcs
citizens-parent
- 2.0.25-SNAPSHOT
+ 2.0.26-SNAPSHOT
citizens-v1_13_R2
diff --git a/v1_14_R1/pom.xml b/v1_14_R1/pom.xml
index 58098ee57..be37bb28a 100644
--- a/v1_14_R1/pom.xml
+++ b/v1_14_R1/pom.xml
@@ -6,7 +6,7 @@
net.citizensnpcs
citizens-parent
- 2.0.25-SNAPSHOT
+ 2.0.26-SNAPSHOT
citizens-v1_14_R1
diff --git a/v1_15_R1/pom.xml b/v1_15_R1/pom.xml
new file mode 100644
index 000000000..efb66a98c
--- /dev/null
+++ b/v1_15_R1/pom.xml
@@ -0,0 +1,86 @@
+
+
+ 4.0.0
+
+ net.citizensnpcs
+ citizens-parent
+ 2.0.26-SNAPSHOT
+
+ citizens-v1_15_R1
+
+
+ UTF-8
+ 1.15-R0.1-SNAPSHOT
+
+
+
+
+ everything
+ http://repo.citizensnpcs.co
+
+
+
+
+
+ ${project.groupId}
+ citizens-main
+ ${project.version}
+ jar
+ provided
+
+
+ org.bukkit
+ craftbukkit
+ ${craftbukkit.version}
+ jar
+ provided
+
+
+
+
+ clean package install
+ ${basedir}/src/main/java
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.0
+
+ eclipse
+
+ 1.8
+
+
+
+ org.codehaus.plexus
+ plexus-compiler-eclipse
+ 2.8.1
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.1.1
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.2.1
+
+
+ package
+
+ shade
+
+
+
+
+
+
+
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/BatController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/BatController.java
new file mode 100644
index 000000000..834299abc
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/BatController.java
@@ -0,0 +1,179 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import net.minecraft.server.v1_15_R1.Vec3D;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftBat;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.Bat;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityBat;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.World;
+
+public class BatController extends MobEntityController {
+ public BatController() {
+ super(EntityBatNPC.class);
+ }
+
+ @Override
+ public Bat getBukkitEntity() {
+ return (Bat) super.getBukkitEntity();
+ }
+
+ public static class BatNPC extends CraftBat implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public BatNPC(EntityBatNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityBatNPC extends EntityBat implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityBatNPC(EntityTypes extends EntityBat> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityBatNPC(EntityTypes extends EntityBat> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ setFlying(false);
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new BatNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null) {
+ return super.isLeashed();
+ }
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ if (npc == null) {
+ super.mobTick();
+ } else {
+ NMSImpl.updateAI(this);
+ npc.update();
+ }
+ }
+
+ public void setFlying(boolean flying) {
+ setAsleep(flying);
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/BeeController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/BeeController.java
new file mode 100644
index 000000000..6eaaf16a1
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/BeeController.java
@@ -0,0 +1,172 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftBee;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.Bee;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityBee;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.World;
+
+public class BeeController extends MobEntityController {
+ public BeeController() {
+ super(EntityBeeNPC.class);
+ }
+
+ @Override
+ public Bee getBukkitEntity() {
+ return (Bee) super.getBukkitEntity();
+ }
+
+ public static class BeeNPC extends CraftBee implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public BeeNPC(EntityBeeNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityBeeNPC extends EntityBee implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityBeeNPC(EntityTypes extends EntityBee> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityBeeNPC(EntityTypes extends EntityBee> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new BeeNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null) {
+ return super.isLeashed();
+ }
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ if (npc == null) {
+ super.mobTick();
+ } else {
+ NMSImpl.updateAI(this);
+ npc.update();
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/BlazeController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/BlazeController.java
new file mode 100644
index 000000000..2d9c97803
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/BlazeController.java
@@ -0,0 +1,170 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import net.minecraft.server.v1_15_R1.Vec3D;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftBlaze;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.Blaze;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityBlaze;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.World;
+
+public class BlazeController extends MobEntityController {
+ public BlazeController() {
+ super(EntityBlazeNPC.class);
+ }
+
+ @Override
+ public Blaze getBukkitEntity() {
+ return (Blaze) super.getBukkitEntity();
+ }
+
+ public static class BlazeNPC extends CraftBlaze implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public BlazeNPC(EntityBlazeNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityBlazeNPC extends EntityBlaze implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityBlazeNPC(EntityTypes extends EntityBlaze> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityBlazeNPC(EntityTypes extends EntityBlaze> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new BlazeNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/CatController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/CatController.java
new file mode 100644
index 000000000..acba1c9d1
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/CatController.java
@@ -0,0 +1,218 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftCat;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.Cat;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityCat;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class CatController extends MobEntityController {
+ public CatController() {
+ super(EntityCatNPC.class);
+ }
+
+ @Override
+ public Cat getBukkitEntity() {
+ return (Cat) super.getBukkitEntity();
+ }
+
+ public static class CatNPC extends CraftCat implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public CatNPC(EntityCatNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityCatNPC extends EntityCat implements NPCHolder {
+ boolean calledNMSHeight = false;
+ private final CitizensNPC npc;
+
+ public EntityCatNPC(EntityTypes extends EntityCat> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityCatNPC(EntityTypes extends EntityCat> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new CatNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/CaveSpiderController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/CaveSpiderController.java
new file mode 100644
index 000000000..e7182c2bd
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/CaveSpiderController.java
@@ -0,0 +1,216 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftCaveSpider;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.CaveSpider;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityCaveSpider;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class CaveSpiderController extends MobEntityController {
+ public CaveSpiderController() {
+ super(EntityCaveSpiderNPC.class);
+ }
+
+ @Override
+ public CaveSpider getBukkitEntity() {
+ return (CaveSpider) super.getBukkitEntity();
+ }
+
+ public static class CaveSpiderNPC extends CraftCaveSpider implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public CaveSpiderNPC(EntityCaveSpiderNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityCaveSpiderNPC extends EntityCaveSpider implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityCaveSpiderNPC(EntityTypes extends EntityCaveSpider> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityCaveSpiderNPC(EntityTypes extends EntityCaveSpider> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new CaveSpiderNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null) {
+ return super.isLeashed();
+ }
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault)) {
+ return super.isLeashed();
+ }
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ChickenController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ChickenController.java
new file mode 100644
index 000000000..24c9da597
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ChickenController.java
@@ -0,0 +1,227 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftChicken;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.Chicken;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityChicken;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class ChickenController extends MobEntityController {
+ public ChickenController() {
+ super(EntityChickenNPC.class);
+ }
+
+ @Override
+ public Chicken getBukkitEntity() {
+ return (Chicken) super.getBukkitEntity();
+ }
+
+ public static class ChickenNPC extends CraftChicken implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public ChickenNPC(EntityChickenNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityChickenNPC extends EntityChicken implements NPCHolder {
+ boolean calledNMSHeight = false;
+
+ private final CitizensNPC npc;
+
+ public EntityChickenNPC(EntityTypes extends EntityChicken> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityChickenNPC(EntityTypes extends EntityChicken> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new ChickenNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void movementTick() {
+ if (npc != null) {
+ this.eggLayTime = 100; // egg timer
+ }
+ super.movementTick();
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/CodController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/CodController.java
new file mode 100644
index 000000000..ac72d76ff
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/CodController.java
@@ -0,0 +1,221 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftCod;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.Cod;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.ControllerMove;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityCod;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class CodController extends MobEntityController {
+ public CodController() {
+ super(EntityCodNPC.class);
+ }
+
+ @Override
+ public Cod getBukkitEntity() {
+ return (Cod) super.getBukkitEntity();
+ }
+
+ public static class CodNPC extends CraftCod implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public CodNPC(EntityCodNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityCodNPC extends EntityCod implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityCodNPC(EntityTypes extends EntityCod> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityCodNPC(EntityTypes extends EntityCod> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ this.moveController = new ControllerMove(this);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new CodNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ if (npc != null) {
+ NMSImpl.setNotInSchool(this);
+ }
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void movementTick() {
+ boolean lastInWater = this.v;
+ if (npc != null) {
+ this.v = false;
+ }
+ super.movementTick();
+ if (npc != null) {
+ this.v = lastInWater;
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/CowController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/CowController.java
new file mode 100644
index 000000000..c2ef10f64
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/CowController.java
@@ -0,0 +1,217 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftCow;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.Cow;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityCow;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class CowController extends MobEntityController {
+ public CowController() {
+ super(EntityCowNPC.class);
+ }
+
+ @Override
+ public Cow getBukkitEntity() {
+ return (Cow) super.getBukkitEntity();
+ }
+
+ public static class CowNPC extends CraftCow implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public CowNPC(EntityCowNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityCowNPC extends EntityCow implements NPCHolder {
+ boolean calledNMSHeight = false;
+ private final CitizensNPC npc;
+
+ public EntityCowNPC(EntityTypes extends EntityCow> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityCowNPC(EntityTypes extends EntityCow> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new CowNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/CreeperController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/CreeperController.java
new file mode 100644
index 000000000..8f7c87207
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/CreeperController.java
@@ -0,0 +1,227 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftCreeper;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.Creeper;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityCreeper;
+import net.minecraft.server.v1_15_R1.EntityLightning;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class CreeperController extends MobEntityController {
+ public CreeperController() {
+ super(EntityCreeperNPC.class);
+ }
+
+ @Override
+ public Creeper getBukkitEntity() {
+ return (Creeper) super.getBukkitEntity();
+ }
+
+ public static class CreeperNPC extends CraftCreeper implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public CreeperNPC(EntityCreeperNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityCreeperNPC extends EntityCreeper implements NPCHolder {
+ private boolean allowPowered;
+ private final CitizensNPC npc;
+
+ public EntityCreeperNPC(EntityTypes extends EntityCreeper> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityCreeperNPC(EntityTypes extends EntityCreeper> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true)) {
+ super.f(x, y, z);
+ }
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new CreeperNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void onLightningStrike(EntityLightning entitylightning) {
+ if (npc == null || allowPowered) {
+ super.onLightningStrike(entitylightning);
+ }
+ }
+
+ public void setAllowPowered(boolean allowPowered) {
+ this.allowPowered = allowPowered;
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/DolphinController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/DolphinController.java
new file mode 100644
index 000000000..81002e8be
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/DolphinController.java
@@ -0,0 +1,223 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftDolphin;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.Dolphin;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.ControllerMove;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityDolphin;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.Navigation;
+import net.minecraft.server.v1_15_R1.NavigationAbstract;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class DolphinController extends MobEntityController {
+ public DolphinController() {
+ super(EntityDolphinNPC.class);
+ }
+
+ @Override
+ public Dolphin getBukkitEntity() {
+ return (Dolphin) super.getBukkitEntity();
+ }
+
+ public static class DolphinNPC extends CraftDolphin implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public DolphinNPC(EntityDolphinNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityDolphinNPC extends EntityDolphin implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityDolphinNPC(EntityTypes extends EntityDolphin> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityDolphinNPC(EntityTypes extends EntityDolphin> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ this.moveController = new ControllerMove(this);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean ay() {
+ return npc == null ? super.ay() : true;
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ protected NavigationAbstract b(World world) {
+ return new Navigation(this, world);
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new DolphinNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isInWater() {
+ return npc == null ? super.isInWater() : false;
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/DrownedController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/DrownedController.java
new file mode 100644
index 000000000..257d74df0
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/DrownedController.java
@@ -0,0 +1,204 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftDrowned;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.Drowned;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityDrowned;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class DrownedController extends MobEntityController {
+ public DrownedController() {
+ super(EntityDrownedNPC.class);
+ }
+
+ @Override
+ public Drowned getBukkitEntity() {
+ return (Drowned) super.getBukkitEntity();
+ }
+
+ public static class DrownedNPC extends CraftDrowned implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public DrownedNPC(EntityDrownedNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityDrownedNPC extends EntityDrowned implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityDrownedNPC(EntityTypes extends EntityDrowned> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityDrownedNPC(EntityTypes extends EntityDrowned> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new DrownedNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/EnderDragonController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/EnderDragonController.java
new file mode 100644
index 000000000..36eefa4c9
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/EnderDragonController.java
@@ -0,0 +1,186 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEnderDragon;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.EnderDragon;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityEnderDragon;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class EnderDragonController extends MobEntityController {
+ public EnderDragonController() {
+ super(EntityEnderDragonNPC.class);
+ }
+
+ @Override
+ public EnderDragon getBukkitEntity() {
+ return (EnderDragon) super.getBukkitEntity();
+ }
+
+ public static class EnderDragonNPC extends CraftEnderDragon implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EnderDragonNPC(EntityEnderDragonNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityEnderDragonNPC extends EntityEnderDragon implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityEnderDragonNPC(EntityTypes extends EntityEnderDragon> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityEnderDragonNPC(EntityTypes extends EntityEnderDragon> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new EnderDragonNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ private float getCorrectYaw(double tX, double tZ) {
+ if (locZ() > tZ)
+ return (float) (-Math.toDegrees(Math.atan((locX() - tX) / (locZ() - tZ))));
+ if (locZ() < tZ) {
+ return (float) (-Math.toDegrees(Math.atan((locX() - tX) / (locX() - tZ)))) + 180.0F;
+ }
+ return yaw;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void movementTick() {
+ if (npc != null) {
+ npc.update();
+ Vec3D mot = getMot();
+ if (mot.getX() != 0 || mot.getY() != 0 || mot.getZ() != 0) {
+ mot = mot.d(0.98, 0.98, 0.98);
+ yaw = getCorrectYaw(locX() + mot.getX(), locZ() + mot.getZ());
+ setPosition(locX() + mot.getX(), locY() + mot.getY(), locZ() + mot.getZ());
+ setMot(mot);
+ }
+ } else {
+ super.movementTick();
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/EndermanController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/EndermanController.java
new file mode 100644
index 000000000..ae1ca5614
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/EndermanController.java
@@ -0,0 +1,221 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEnderman;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.Enderman;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityEnderman;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class EndermanController extends MobEntityController {
+ public EndermanController() {
+ super(EntityEndermanNPC.class);
+ }
+
+ @Override
+ public Enderman getBukkitEntity() {
+ return (Enderman) super.getBukkitEntity();
+ }
+
+ public static class EndermanNPC extends CraftEnderman implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EndermanNPC(EntityEndermanNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityEndermanNPC extends EntityEnderman implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityEndermanNPC(EntityTypes extends EntityEnderman> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityEndermanNPC(EntityTypes extends EntityEnderman> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean a(double d1, double d2, double d3, boolean b) {
+ if (npc == null) {
+ return super.a(d1, d2, d3, b);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new EndermanNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/EndermiteController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/EndermiteController.java
new file mode 100644
index 000000000..a3cf98d11
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/EndermiteController.java
@@ -0,0 +1,212 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEndermite;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.Endermite;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityEndermite;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class EndermiteController extends MobEntityController {
+ public EndermiteController() {
+ super(EntityEndermiteNPC.class);
+ }
+
+ @Override
+ public Endermite getBukkitEntity() {
+ return (Endermite) super.getBukkitEntity();
+ }
+
+ public static class EndermiteNPC extends CraftEndermite implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EndermiteNPC(EntityEndermiteNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityEndermiteNPC extends EntityEndermite implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityEndermiteNPC(EntityTypes extends EntityEndermite> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityEndermiteNPC(EntityTypes extends EntityEndermite> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new EndermiteNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null)
+ npc.update();
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/EntityHumanNPC.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/EntityHumanNPC.java
new file mode 100644
index 000000000..8e32ed225
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/EntityHumanNPC.java
@@ -0,0 +1,549 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.util.List;
+import java.util.Map;
+
+import org.bukkit.Bukkit;
+import org.bukkit.ChatColor;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+import org.bukkit.metadata.MetadataValue;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.util.Vector;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import com.mojang.authlib.GameProfile;
+
+import net.citizensnpcs.Settings.Setting;
+import net.citizensnpcs.api.CitizensAPI;
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.MetadataStore;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.api.trait.trait.Inventory;
+import net.citizensnpcs.nms.v1_15_R1.network.EmptyNetHandler;
+import net.citizensnpcs.nms.v1_15_R1.network.EmptyNetworkManager;
+import net.citizensnpcs.nms.v1_15_R1.network.EmptySocket;
+import net.citizensnpcs.nms.v1_15_R1.util.EmptyAdvancementDataPlayer;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.nms.v1_15_R1.util.PlayerControllerJump;
+import net.citizensnpcs.nms.v1_15_R1.util.PlayerControllerLook;
+import net.citizensnpcs.nms.v1_15_R1.util.PlayerControllerMove;
+import net.citizensnpcs.nms.v1_15_R1.util.PlayerNavigation;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.npc.skin.SkinPacketTracker;
+import net.citizensnpcs.npc.skin.SkinnableEntity;
+import net.citizensnpcs.trait.Gravity;
+import net.citizensnpcs.util.NMS;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.AttributeInstance;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.ChatComponentText;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.Entity;
+import net.minecraft.server.v1_15_R1.EntityHuman;
+import net.minecraft.server.v1_15_R1.EntityPlayer;
+import net.minecraft.server.v1_15_R1.EnumGamemode;
+import net.minecraft.server.v1_15_R1.EnumItemSlot;
+import net.minecraft.server.v1_15_R1.EnumProtocolDirection;
+import net.minecraft.server.v1_15_R1.GenericAttributes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.IChatBaseComponent;
+import net.minecraft.server.v1_15_R1.MathHelper;
+import net.minecraft.server.v1_15_R1.MinecraftServer;
+import net.minecraft.server.v1_15_R1.NavigationAbstract;
+import net.minecraft.server.v1_15_R1.NetworkManager;
+import net.minecraft.server.v1_15_R1.Packet;
+import net.minecraft.server.v1_15_R1.PacketPlayOutEntityEquipment;
+import net.minecraft.server.v1_15_R1.PacketPlayOutEntityHeadRotation;
+import net.minecraft.server.v1_15_R1.PathType;
+import net.minecraft.server.v1_15_R1.PlayerInteractManager;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.WorldServer;
+
+public class EntityHumanNPC extends EntityPlayer implements NPCHolder, SkinnableEntity {
+ private final Map bz = Maps.newEnumMap(PathType.class);
+ private PlayerControllerJump controllerJump;
+ private PlayerControllerLook controllerLook;
+ private PlayerControllerMove controllerMove;
+ private boolean isTracked = false;
+ private int jumpTicks = 0;
+ private PlayerNavigation navigation;
+ private final CitizensNPC npc;
+ private final Location packetLocationCache = new Location(null, 0, 0, 0);
+ private final SkinPacketTracker skinTracker;
+ private int updateCounter = 0;
+
+ public EntityHumanNPC(MinecraftServer minecraftServer, WorldServer world, GameProfile gameProfile,
+ PlayerInteractManager playerInteractManager, NPC npc) {
+ super(minecraftServer, world, gameProfile, playerInteractManager);
+
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ skinTracker = new SkinPacketTracker(this);
+ playerInteractManager.setGameMode(EnumGamemode.SURVIVAL);
+ initialise(minecraftServer);
+ } else {
+ skinTracker = null;
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean a(EntityPlayer entityplayer) {
+ if (npc != null && !isTracked) {
+ return false;
+ }
+ return super.a(entityplayer);
+ }
+
+ public float a(PathType pathtype) {
+ return this.bz.containsKey(pathtype) ? this.bz.get(pathtype).floatValue() : pathtype.a();
+ }
+
+ public void a(PathType pathtype, float f) {
+ this.bz.put(pathtype, Float.valueOf(f));
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean damageEntity(DamageSource damagesource, float f) {
+ // knock back velocity is cancelled and sent to client for handling when
+ // the entity is a player. there is no client so make this happen
+ // manually.
+ boolean damaged = super.damageEntity(damagesource, f);
+ if (damaged && velocityChanged) {
+ velocityChanged = false;
+ Bukkit.getScheduler().runTask(CitizensAPI.getPlugin(), new Runnable() {
+ @Override
+ public void run() {
+ EntityHumanNPC.this.velocityChanged = true;
+ }
+ });
+ }
+ return damaged;
+ }
+
+ @Override
+ public void die() {
+ super.die();
+ getAdvancementData().a();
+ }
+
+ @Override
+ public void die(DamageSource damagesource) {
+ // players that die are not normally removed from the world. when the
+ // NPC dies, we are done with the instance and it should be removed.
+ if (dead) {
+ return;
+ }
+ super.die(damagesource);
+ Bukkit.getScheduler().runTaskLater(CitizensAPI.getPlugin(), new Runnable() {
+ @Override
+ public void run() {
+ ((WorldServer) world).removeEntity(EntityHumanNPC.this);
+ }
+ }, 35); // give enough time for death and smoke animation
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true)) {
+ super.f(x, y, z);
+ }
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftPlayer getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new PlayerNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ public PlayerControllerJump getControllerJump() {
+ return controllerJump;
+ }
+
+ public PlayerControllerMove getControllerMove() {
+ return controllerMove;
+ }
+
+ public NavigationAbstract getNavigation() {
+ return navigation;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public IChatBaseComponent getPlayerListName() {
+ if (Setting.REMOVE_PLAYERS_FROM_PLAYER_LIST.asBoolean()) {
+ return new ChatComponentText("");
+ }
+ return super.getPlayerListName();
+ }
+
+ @Override
+ public String getSkinName() {
+ MetadataStore meta = npc.data();
+
+ String skinName = meta.get(NPC.PLAYER_SKIN_UUID_METADATA);
+ if (skinName == null) {
+ skinName = ChatColor.stripColor(getName());
+ }
+ return skinName.toLowerCase();
+ }
+
+ @Override
+ public SkinPacketTracker getSkinTracker() {
+ return skinTracker;
+ }
+
+ private void initialise(MinecraftServer minecraftServer) {
+ Socket socket = new EmptySocket();
+ NetworkManager conn = null;
+ try {
+ conn = new EmptyNetworkManager(EnumProtocolDirection.CLIENTBOUND);
+ playerConnection = new EmptyNetHandler(minecraftServer, conn, this);
+ conn.setPacketListener(playerConnection);
+ socket.close();
+ } catch (IOException e) {
+ // swallow
+ }
+
+ AttributeInstance range = getAttributeInstance(GenericAttributes.FOLLOW_RANGE);
+ if (range == null) {
+ range = getAttributeMap().b(GenericAttributes.FOLLOW_RANGE);
+ }
+ range.setValue(Setting.DEFAULT_PATHFINDING_RANGE.asDouble());
+
+ controllerJump = new PlayerControllerJump(this);
+ controllerLook = new PlayerControllerLook(this);
+ controllerMove = new PlayerControllerMove(this);
+ navigation = new PlayerNavigation(this, world);
+ NMS.setStepHeight(getBukkitEntity(), 1); // the default (0) breaks step climbing
+ setSkinFlags((byte) 0xFF);
+
+ EmptyAdvancementDataPlayer.clear(this.getAdvancementData());
+ NMSImpl.setAdvancement(this.getBukkitEntity(),
+ new EmptyAdvancementDataPlayer(minecraftServer, CitizensAPI.getDataFolder().getParentFile(), this));
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isCollidable() {
+ return npc == null ? super.isCollidable() : npc.data().get(NPC.COLLIDABLE_METADATA, true);
+ }
+
+ public boolean isNavigating() {
+ return npc.getNavigator().isNavigating();
+ }
+
+ public void livingEntityBaseTick() {
+ entityBaseTick();
+ this.az = this.aA;
+ if (this.hurtTicks > 0) {
+ this.hurtTicks -= 1;
+ }
+ tickPotionEffects();
+ this.aU = this.aT;
+ this.aJ = this.aI;
+ this.aL = this.aK;
+ this.lastYaw = this.yaw;
+ this.lastPitch = this.pitch;
+ }
+
+ private void moveOnCurrentHeading() {
+ if (jumping) {
+ if (onGround && jumpTicks == 0) {
+ jump();
+ jumpTicks = 10;
+ }
+ } else {
+ jumpTicks = 0;
+ }
+ aZ *= 0.98F;
+ bb *= 0.98F;
+ e(new Vec3D(this.aZ, this.ba, this.bb)); // movement method
+ NMS.setHeadYaw(getBukkitEntity(), yaw);
+ if (jumpTicks > 0) {
+ jumpTicks--;
+ }
+ }
+
+ public void setMoveDestination(double x, double y, double z, double speed) {
+ controllerMove.a(x, y, z, speed);
+ }
+
+ public void setShouldJump() {
+ controllerJump.jump();
+ }
+
+ @Override
+ public void setSkinFlags(byte flags) {
+ // set skin flag byte
+ getDataWatcher().set(EntityHuman.bq, flags);
+ }
+
+ @Override
+ public void setSkinName(String name) {
+ setSkinName(name, false);
+ }
+
+ @Override
+ public void setSkinName(String name, boolean forceUpdate) {
+ Preconditions.checkNotNull(name);
+
+ npc.data().setPersistent(NPC.PLAYER_SKIN_UUID_METADATA, name.toLowerCase());
+ skinTracker.notifySkinChange(forceUpdate);
+ }
+
+ @Override
+ public void setSkinPersistent(String skinName, String signature, String data) {
+ Preconditions.checkNotNull(skinName);
+ Preconditions.checkNotNull(signature);
+ Preconditions.checkNotNull(data);
+
+ npc.data().setPersistent(NPC.PLAYER_SKIN_UUID_METADATA, skinName.toLowerCase());
+ npc.data().setPersistent(NPC.PLAYER_SKIN_TEXTURE_PROPERTIES_SIGN_METADATA, signature);
+ npc.data().setPersistent(NPC.PLAYER_SKIN_TEXTURE_PROPERTIES_METADATA, data);
+ npc.data().setPersistent(NPC.PLAYER_SKIN_USE_LATEST, false);
+ npc.data().setPersistent("cached-skin-uuid-name", skinName.toLowerCase());
+ skinTracker.notifySkinChange(false);
+ }
+
+ public void setTargetLook(Entity target, float yawOffset, float renderOffset) {
+ controllerLook.a(target, yawOffset, renderOffset);
+ }
+
+ public void setTargetLook(Location target) {
+ controllerLook.a(target.getX(), target.getY(), target.getZ(), 10, 40);
+ }
+
+ public void setTracked() {
+ isTracked = true;
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+ if (npc == null)
+ return;
+ this.noclip = isSpectator();
+ if (updateCounter + 1 > Setting.PACKET_UPDATE_DELAY.asInt()) {
+ updateEffects = true;
+ }
+ Bukkit.getServer().getPluginManager().unsubscribeFromPermission("bukkit.broadcast.user", getBukkitEntity());
+ livingEntityBaseTick();
+
+ boolean navigating = npc.getNavigator().isNavigating();
+ updatePackets(navigating);
+ if (!navigating && getBukkitEntity() != null && npc.getTrait(Gravity.class).hasGravity()
+ && Util.isLoaded(getBukkitEntity().getLocation(LOADED_LOCATION))) {
+ e(new Vec3D(0, 0, 0));
+ }
+ Vec3D mot = getMot();
+ if (Math.abs(mot.getX()) < EPSILON && Math.abs(mot.getY()) < EPSILON && Math.abs(mot.getZ()) < EPSILON) {
+ setMot(new Vec3D(0, 0, 0));
+ }
+ if (navigating) {
+ if (!NMSImpl.isNavigationFinished(navigation)) {
+ NMSImpl.updateNavigation(navigation);
+ }
+ moveOnCurrentHeading();
+ }
+ NMSImpl.updateAI(this);
+
+ if (noDamageTicks > 0) {
+ --noDamageTicks;
+ }
+
+ npc.update();
+ }
+
+ public void updateAI() {
+ controllerMove.a();
+ controllerLook.a();
+ controllerJump.b();
+ }
+
+ private void updatePackets(boolean navigating) {
+ if (updateCounter++ <= Setting.PACKET_UPDATE_DELAY.asInt())
+ return;
+
+ updateCounter = 0;
+ Location current = getBukkitEntity().getLocation(packetLocationCache);
+ Packet>[] packets = new Packet[navigating ? EnumItemSlot.values().length : EnumItemSlot.values().length + 1];
+ if (!navigating) {
+ packets[5] = new PacketPlayOutEntityHeadRotation(this,
+ (byte) MathHelper.d(NMSImpl.getHeadYaw(this) * 256.0F / 360.0F));
+ }
+ int i = 0;
+ for (EnumItemSlot slot : EnumItemSlot.values()) {
+ packets[i++] = new PacketPlayOutEntityEquipment(getId(), slot, getEquipment(slot));
+ }
+ NMSImpl.sendPacketsNearby(getBukkitEntity(), current, packets);
+ }
+
+ public void updatePathfindingRange(float pathfindingRange) {
+ this.navigation.setRange(pathfindingRange);
+ }
+
+ public static class PlayerNPC extends CraftPlayer implements NPCHolder, SkinnableEntity {
+ private final CraftServer cserver;
+ private final CitizensNPC npc;
+
+ private PlayerNPC(EntityHumanNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ this.cserver = (CraftServer) Bukkit.getServer();
+ npc.getTrait(Inventory.class);
+ }
+
+ @Override
+ public Player getBukkitEntity() {
+ return this;
+ }
+
+ @Override
+ public EntityHumanNPC getHandle() {
+ return (EntityHumanNPC) this.entity;
+ }
+
+ @Override
+ public List getMetadata(String metadataKey) {
+ return cserver.getEntityMetadata().getMetadata(this, metadataKey);
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public String getSkinName() {
+ return ((SkinnableEntity) this.entity).getSkinName();
+ }
+
+ @Override
+ public SkinPacketTracker getSkinTracker() {
+ return ((SkinnableEntity) this.entity).getSkinTracker();
+ }
+
+ @Override
+ public boolean hasMetadata(String metadataKey) {
+ return cserver.getEntityMetadata().hasMetadata(this, metadataKey);
+ }
+
+ @Override
+ public void removeMetadata(String metadataKey, Plugin owningPlugin) {
+ cserver.getEntityMetadata().removeMetadata(this, metadataKey, owningPlugin);
+ }
+
+ @Override
+ public void setMetadata(String metadataKey, MetadataValue newMetadataValue) {
+ cserver.getEntityMetadata().setMetadata(this, metadataKey, newMetadataValue);
+ }
+
+ @Override
+ public void setSkinFlags(byte flags) {
+ ((SkinnableEntity) this.entity).setSkinFlags(flags);
+ }
+
+ @Override
+ public void setSkinName(String name) {
+ ((SkinnableEntity) this.entity).setSkinName(name);
+ }
+
+ @Override
+ public void setSkinName(String skinName, boolean forceUpdate) {
+ ((SkinnableEntity) this.entity).setSkinName(skinName, forceUpdate);
+ }
+
+ @Override
+ public void setSkinPersistent(String skinName, String signature, String data) {
+ ((SkinnableEntity) this.entity).setSkinPersistent(skinName, signature, data);
+ }
+ }
+
+ private static final float EPSILON = 0.005F;
+ private static final Location LOADED_LOCATION = new Location(null, 0, 0, 0);
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/EvokerController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/EvokerController.java
new file mode 100644
index 000000000..f6d584103
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/EvokerController.java
@@ -0,0 +1,205 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEvoker;
+import org.bukkit.entity.Evoker;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityEvoker;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class EvokerController extends MobEntityController {
+ public EvokerController() {
+ super(EntityEvokerNPC.class);
+ }
+
+ @Override
+ public Evoker getBukkitEntity() {
+ return (Evoker) super.getBukkitEntity();
+ }
+
+ public static class EntityEvokerNPC extends EntityEvoker implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityEvokerNPC(EntityTypes extends EntityEvoker> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityEvokerNPC(EntityTypes extends EntityEvoker> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new EvokerNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+
+ public static class EvokerNPC extends CraftEvoker implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EvokerNPC(EntityEvokerNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/FoxController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/FoxController.java
new file mode 100644
index 000000000..1d261dd43
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/FoxController.java
@@ -0,0 +1,218 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftFox;
+import org.bukkit.entity.Fox;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityFox;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class FoxController extends MobEntityController {
+ public FoxController() {
+ super(EntityFoxNPC.class);
+ }
+
+ @Override
+ public Fox getBukkitEntity() {
+ return (Fox) super.getBukkitEntity();
+ }
+
+ public static class EntityFoxNPC extends EntityFox implements NPCHolder {
+ boolean calledNMSHeight = false;
+ private final CitizensNPC npc;
+
+ public EntityFoxNPC(EntityTypes extends EntityFox> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityFoxNPC(EntityTypes extends EntityFox> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new FoxNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+
+ public static class FoxNPC extends CraftFox implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public FoxNPC(EntityFoxNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/GhastController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/GhastController.java
new file mode 100644
index 000000000..ac28e4100
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/GhastController.java
@@ -0,0 +1,176 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import net.minecraft.server.v1_15_R1.Vec3D;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftGhast;
+import org.bukkit.entity.Ghast;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityGhast;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.World;
+
+public class GhastController extends MobEntityController {
+ public GhastController() {
+ super(EntityGhastNPC.class);
+ }
+
+ @Override
+ public Ghast getBukkitEntity() {
+ return (Ghast) super.getBukkitEntity();
+ }
+
+ public static class EntityGhastNPC extends EntityGhast implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityGhastNPC(EntityTypes extends EntityGhast> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityGhastNPC(EntityTypes extends EntityGhast> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new GhastNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public boolean isRiptiding() {
+ return npc != null;
+ }
+
+ @Override
+ public void mobTick() {
+ if (npc != null) {
+ npc.update();
+ }
+ super.mobTick();
+ }
+ }
+
+ public static class GhastNPC extends CraftGhast implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public GhastNPC(EntityGhastNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/GiantController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/GiantController.java
new file mode 100644
index 000000000..eb02c68fa
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/GiantController.java
@@ -0,0 +1,213 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftGiant;
+import org.bukkit.entity.Giant;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityGiantZombie;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class GiantController extends MobEntityController {
+ public GiantController() {
+ super(EntityGiantNPC.class);
+ }
+
+ @Override
+ public Giant getBukkitEntity() {
+ return (Giant) super.getBukkitEntity();
+ }
+
+ public static class EntityGiantNPC extends EntityGiantZombie implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityGiantNPC(EntityTypes extends EntityGiantZombie> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityGiantNPC(EntityTypes extends EntityGiantZombie> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new GiantNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+ }
+
+ public static class GiantNPC extends CraftGiant implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public GiantNPC(EntityGiantNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/GuardianController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/GuardianController.java
new file mode 100644
index 000000000..7c150fed9
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/GuardianController.java
@@ -0,0 +1,216 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftGuardian;
+import org.bukkit.entity.Guardian;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityGuardian;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class GuardianController extends MobEntityController {
+ public GuardianController() {
+ super(EntityGuardianNPC.class);
+ }
+
+ @Override
+ public Guardian getBukkitEntity() {
+ return (Guardian) super.getBukkitEntity();
+ }
+
+ public static class EntityGuardianNPC extends EntityGuardian implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityGuardianNPC(EntityTypes extends EntityGuardian> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityGuardianNPC(EntityTypes extends EntityGuardian> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new GuardianNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void movementTick() {
+ if (npc == null) {
+ super.movementTick();
+ } else {
+ NMSImpl.updateAI(this);
+ npc.update();
+ }
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+ }
+
+ public static class GuardianNPC extends CraftGuardian implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public GuardianNPC(EntityGuardianNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/GuardianElderController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/GuardianElderController.java
new file mode 100644
index 000000000..fdc791389
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/GuardianElderController.java
@@ -0,0 +1,216 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftElderGuardian;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.ElderGuardian;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityGuardianElder;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class GuardianElderController extends MobEntityController {
+ public GuardianElderController() {
+ super(EntityGuardianElderNPC.class);
+ }
+
+ @Override
+ public ElderGuardian getBukkitEntity() {
+ return (ElderGuardian) super.getBukkitEntity();
+ }
+
+ public static class EntityGuardianElderNPC extends EntityGuardianElder implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityGuardianElderNPC(EntityTypes extends EntityGuardianElder> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityGuardianElderNPC(EntityTypes extends EntityGuardianElder> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new GuardianElderNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void movementTick() {
+ if (npc == null) {
+ super.movementTick();
+ } else {
+ NMSImpl.updateAI(this);
+ npc.update();
+ }
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+ }
+
+ public static class GuardianElderNPC extends CraftElderGuardian implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public GuardianElderNPC(EntityGuardianElderNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/HorseController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/HorseController.java
new file mode 100644
index 000000000..09be21ffe
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/HorseController.java
@@ -0,0 +1,253 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftHorse;
+import org.bukkit.entity.Horse;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.trait.Controllable;
+import net.citizensnpcs.trait.HorseModifiers;
+import net.citizensnpcs.util.NMS;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityHorse;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.GenericAttributes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class HorseController extends MobEntityController {
+ public HorseController() {
+ super(EntityHorseNPC.class);
+ }
+
+ @Override
+ public Horse getBukkitEntity() {
+ return (Horse) super.getBukkitEntity();
+ }
+
+ @Override
+ public void spawn(Location at, NPC npc) {
+ npc.getTrait(HorseModifiers.class);
+ super.spawn(at, npc);
+ }
+
+ public static class EntityHorseNPC extends EntityHorse implements NPCHolder {
+ private double baseMovementSpeed;
+ private boolean calledNMSHeight = false;
+ private final CitizensNPC npc;
+ private boolean riding;
+
+ public EntityHorseNPC(EntityTypes extends EntityHorse> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityHorseNPC(EntityTypes extends EntityHorse> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ Horse horse = (Horse) getBukkitEntity();
+ horse.setDomestication(horse.getMaxDomestication());
+ baseMovementSpeed = this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue();
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean ca() {
+ if (npc != null && riding) {
+ return true;
+ }
+ return super.ca();
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new HorseNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ if (npc.hasTrait(Controllable.class) && npc.getTrait(Controllable.class).isEnabled()) {
+ riding = getBukkitEntity().getPassengers().size() > 0;
+ getAttributeInstance(GenericAttributes.MOVEMENT_SPEED)
+ .setValue(baseMovementSpeed * npc.getNavigator().getDefaultParameters().speedModifier());
+ } else {
+ riding = false;
+ }
+ if (riding) {
+ d(4, true); // datawatcher method
+ }
+ NMS.setStepHeight(getBukkitEntity(), 1);
+ npc.update();
+ }
+ }
+ }
+
+ public static class HorseNPC extends CraftHorse implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public HorseNPC(EntityHorseNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/HorseDonkeyController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/HorseDonkeyController.java
new file mode 100644
index 000000000..c44905251
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/HorseDonkeyController.java
@@ -0,0 +1,254 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftDonkey;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.Donkey;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.trait.Controllable;
+import net.citizensnpcs.trait.HorseModifiers;
+import net.citizensnpcs.util.NMS;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityHorseDonkey;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.GenericAttributes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class HorseDonkeyController extends MobEntityController {
+ public HorseDonkeyController() {
+ super(EntityHorseDonkeyNPC.class);
+ }
+
+ @Override
+ public Donkey getBukkitEntity() {
+ return (Donkey) super.getBukkitEntity();
+ }
+
+ @Override
+ public void spawn(Location at, NPC npc) {
+ npc.addTrait(HorseModifiers.class);
+ super.spawn(at, npc);
+ }
+
+ public static class EntityHorseDonkeyNPC extends EntityHorseDonkey implements NPCHolder {
+ private double baseMovementSpeed;
+ boolean calledNMSHeight = false;
+
+ private final CitizensNPC npc;
+
+ private boolean riding;
+
+ public EntityHorseDonkeyNPC(EntityTypes extends EntityHorseDonkey> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityHorseDonkeyNPC(EntityTypes extends EntityHorseDonkey> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ ((Donkey) getBukkitEntity()).setDomestication(((Donkey) getBukkitEntity()).getMaxDomestication());
+ baseMovementSpeed = this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue();
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean ca() {
+ if (npc != null && riding) {
+ return true;
+ }
+ return super.ca();
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new HorseDonkeyNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ if (npc.hasTrait(Controllable.class) && npc.getTrait(Controllable.class).isEnabled()) {
+ riding = getBukkitEntity().getPassengers().size() > 0;
+ getAttributeInstance(GenericAttributes.MOVEMENT_SPEED)
+ .setValue(baseMovementSpeed * npc.getNavigator().getDefaultParameters().speedModifier());
+ } else {
+ riding = false;
+ }
+ if (riding) {
+ d(4, true); // datawatcher method
+ }
+ NMS.setStepHeight(getBukkitEntity(), 1);
+ npc.update();
+ }
+ }
+ }
+
+ public static class HorseDonkeyNPC extends CraftDonkey implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public HorseDonkeyNPC(EntityHorseDonkeyNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/HorseMuleController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/HorseMuleController.java
new file mode 100644
index 000000000..f902ca346
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/HorseMuleController.java
@@ -0,0 +1,252 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftMule;
+import org.bukkit.entity.Mule;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.trait.Controllable;
+import net.citizensnpcs.trait.HorseModifiers;
+import net.citizensnpcs.util.NMS;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityHorseMule;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.GenericAttributes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class HorseMuleController extends MobEntityController {
+ public HorseMuleController() {
+ super(EntityHorseMuleNPC.class);
+ }
+
+ @Override
+ public Mule getBukkitEntity() {
+ return (Mule) super.getBukkitEntity();
+ }
+
+ @Override
+ public void spawn(Location at, NPC npc) {
+ npc.getTrait(HorseModifiers.class);
+ super.spawn(at, npc);
+ }
+
+ public static class EntityHorseMuleNPC extends EntityHorseMule implements NPCHolder {
+ private double baseMovementSpeed;
+ boolean calledNMSHeight = false;
+ private final CitizensNPC npc;
+ private boolean riding;
+
+ public EntityHorseMuleNPC(EntityTypes extends EntityHorseMule> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityHorseMuleNPC(EntityTypes extends EntityHorseMule> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ ((Mule) getBukkitEntity()).setDomestication(((Mule) getBukkitEntity()).getMaxDomestication());
+ baseMovementSpeed = this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue();
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean ca() {
+ if (npc != null && riding) {
+ return true;
+ }
+ return super.ca();
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new HorseMuleNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ if (npc.hasTrait(Controllable.class) && npc.getTrait(Controllable.class).isEnabled()) {
+ riding = getBukkitEntity().getPassengers().size() > 0;
+ getAttributeInstance(GenericAttributes.MOVEMENT_SPEED)
+ .setValue(baseMovementSpeed * npc.getNavigator().getDefaultParameters().speedModifier());
+ } else {
+ riding = false;
+ }
+ if (riding) {
+ d(4, true); // datawatcher method
+ }
+ NMS.setStepHeight(getBukkitEntity(), 1);
+ npc.update();
+ }
+ }
+ }
+
+ public static class HorseMuleNPC extends CraftMule implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public HorseMuleNPC(EntityHorseMuleNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/HorseSkeletonController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/HorseSkeletonController.java
new file mode 100644
index 000000000..0d74278ff
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/HorseSkeletonController.java
@@ -0,0 +1,256 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftSkeletonHorse;
+import org.bukkit.entity.SkeletonHorse;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.trait.Controllable;
+import net.citizensnpcs.trait.HorseModifiers;
+import net.citizensnpcs.util.NMS;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityHorseSkeleton;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.GenericAttributes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class HorseSkeletonController extends MobEntityController {
+ public HorseSkeletonController() {
+ super(EntityHorseSkeletonNPC.class);
+ }
+
+ @Override
+ public SkeletonHorse getBukkitEntity() {
+ return (SkeletonHorse) super.getBukkitEntity();
+ }
+
+ @Override
+ public void spawn(Location at, NPC npc) {
+ npc.getTrait(HorseModifiers.class);
+ super.spawn(at, npc);
+ }
+
+ public static class EntityHorseSkeletonNPC extends EntityHorseSkeleton implements NPCHolder {
+ private double baseMovementSpeed;
+
+ boolean calledNMSHeight = false;
+
+ private final CitizensNPC npc;
+
+ private boolean riding;
+
+ public EntityHorseSkeletonNPC(EntityTypes extends EntityHorseSkeleton> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityHorseSkeletonNPC(EntityTypes extends EntityHorseSkeleton> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ ((SkeletonHorse) getBukkitEntity())
+ .setDomestication(((SkeletonHorse) getBukkitEntity()).getMaxDomestication());
+ baseMovementSpeed = this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue();
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean ca() {
+ if (npc != null && riding) {
+ return true;
+ }
+ return super.ca();
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new HorseSkeletonNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ if (npc.hasTrait(Controllable.class) && npc.getTrait(Controllable.class).isEnabled()) {
+ riding = getBukkitEntity().getPassengers().size() > 0;
+ getAttributeInstance(GenericAttributes.MOVEMENT_SPEED)
+ .setValue(baseMovementSpeed * npc.getNavigator().getDefaultParameters().speedModifier());
+ } else {
+ riding = false;
+ }
+ if (riding) {
+ d(4, true); // datawatcher method
+ }
+ NMS.setStepHeight(getBukkitEntity(), 1);
+ npc.update();
+ }
+ }
+ }
+
+ public static class HorseSkeletonNPC extends CraftSkeletonHorse implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public HorseSkeletonNPC(EntityHorseSkeletonNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/HorseZombieController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/HorseZombieController.java
new file mode 100644
index 000000000..f50d15463
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/HorseZombieController.java
@@ -0,0 +1,256 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftZombieHorse;
+import org.bukkit.entity.ZombieHorse;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.trait.Controllable;
+import net.citizensnpcs.trait.HorseModifiers;
+import net.citizensnpcs.util.NMS;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityHorseZombie;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.GenericAttributes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class HorseZombieController extends MobEntityController {
+ public HorseZombieController() {
+ super(EntityHorseZombieNPC.class);
+ }
+
+ @Override
+ public ZombieHorse getBukkitEntity() {
+ return (ZombieHorse) super.getBukkitEntity();
+ }
+
+ @Override
+ public void spawn(Location at, NPC npc) {
+ npc.getTrait(HorseModifiers.class);
+ super.spawn(at, npc);
+ }
+
+ public static class EntityHorseZombieNPC extends EntityHorseZombie implements NPCHolder {
+ private double baseMovementSpeed;
+
+ boolean calledNMSHeight = false;
+
+ private final CitizensNPC npc;
+
+ private boolean riding;
+
+ public EntityHorseZombieNPC(EntityTypes extends EntityHorseZombie> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityHorseZombieNPC(EntityTypes extends EntityHorseZombie> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ ((ZombieHorse) getBukkitEntity())
+ .setDomestication(((ZombieHorse) getBukkitEntity()).getMaxDomestication());
+ baseMovementSpeed = this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue();
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean ca() {
+ if (npc != null && riding) {
+ return true;
+ }
+ return super.ca();
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new HorseZombieNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ if (npc.hasTrait(Controllable.class) && npc.getTrait(Controllable.class).isEnabled()) {
+ riding = getBukkitEntity().getPassengers().size() > 0;
+ getAttributeInstance(GenericAttributes.MOVEMENT_SPEED)
+ .setValue(baseMovementSpeed * npc.getNavigator().getDefaultParameters().speedModifier());
+ } else {
+ riding = false;
+ }
+ if (riding) {
+ d(4, true); // datawatcher method
+ }
+ NMS.setStepHeight(getBukkitEntity(), 1);
+ npc.update();
+ }
+ }
+ }
+
+ public static class HorseZombieNPC extends CraftZombieHorse implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public HorseZombieNPC(EntityHorseZombieNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/HumanController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/HumanController.java
new file mode 100644
index 000000000..2996fcaf3
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/HumanController.java
@@ -0,0 +1,126 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import net.minecraft.server.v1_15_R1.Vec3D;
+
+import java.util.UUID;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import org.bukkit.scoreboard.Scoreboard;
+import org.bukkit.scoreboard.Team;
+
+import com.mojang.authlib.GameProfile;
+
+import net.citizensnpcs.Settings.Setting;
+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.npc.skin.Skin;
+import net.citizensnpcs.npc.skin.SkinnableEntity;
+import net.citizensnpcs.util.NMS;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.PlayerInteractManager;
+import net.minecraft.server.v1_15_R1.WorldServer;
+
+public class HumanController extends AbstractEntityController {
+ public HumanController() {
+ super();
+ }
+
+ @Override
+ protected Entity createEntity(final Location at, final NPC npc) {
+ final WorldServer nmsWorld = ((CraftWorld) at.getWorld()).getHandle();
+ String coloredName = Colorizer.parseColors(npc.getFullName());
+
+ String[] nameSplit = Util.splitPlayerName(coloredName);
+ String name = nameSplit[0];
+
+ final String prefixCapture = nameSplit[1], suffixCapture = nameSplit[2];
+
+ UUID uuid = npc.getUniqueId();
+ if (uuid.version() == 4) { // clear version
+ long msb = uuid.getMostSignificantBits();
+ msb &= ~0x0000000000004000L;
+ msb |= 0x0000000000002000L;
+ uuid = new UUID(msb, uuid.getLeastSignificantBits());
+ }
+
+ final GameProfile profile = new GameProfile(uuid, name);
+
+ final EntityHumanNPC handle = new EntityHumanNPC(nmsWorld.getServer().getServer(), nmsWorld, profile,
+ new PlayerInteractManager(nmsWorld), npc);
+
+ Skin skin = handle.getSkinTracker().getSkin();
+ if (skin != null) {
+ skin.apply(handle);
+ }
+
+ Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), new Runnable() {
+ @Override
+ public void run() {
+ if (getBukkitEntity() == null || !getBukkitEntity().isValid())
+ return;
+ boolean removeFromPlayerList = npc.data().get("removefromplayerlist",
+ Setting.REMOVE_PLAYERS_FROM_PLAYER_LIST.asBoolean());
+ NMS.addOrRemoveFromPlayerList(getBukkitEntity(), removeFromPlayerList);
+
+ if (Setting.USE_SCOREBOARD_TEAMS.asBoolean()) {
+ Scoreboard scoreboard = Bukkit.getScoreboardManager().getMainScoreboard();
+ String teamName = profile.getId().toString().substring(0, 16);
+
+ Team team = scoreboard.getTeam(teamName);
+ if (team == null) {
+ team = scoreboard.registerNewTeam(teamName);
+ }
+ if (prefixCapture != null) {
+ team.setPrefix(prefixCapture);
+ }
+ if (suffixCapture != null) {
+ team.setSuffix(suffixCapture);
+ }
+ team.addPlayer(handle.getBukkitEntity());
+
+ handle.getNPC().data().set(NPC.SCOREBOARD_FAKE_TEAM_NAME_METADATA, teamName);
+ }
+ }
+ }, 20);
+
+ handle.getBukkitEntity().setSleepingIgnored(true);
+
+ return handle.getBukkitEntity();
+ }
+
+ @Override
+ public Player getBukkitEntity() {
+ return (Player) super.getBukkitEntity();
+ }
+
+ @Override
+ public void remove() {
+ Player entity = getBukkitEntity();
+ if (entity != null) {
+ if (Setting.USE_SCOREBOARD_TEAMS.asBoolean()) {
+ String teamName = entity.getUniqueId().toString().substring(0, 16);
+ Scoreboard scoreboard = Bukkit.getScoreboardManager().getMainScoreboard();
+ Team team = scoreboard.getTeam(teamName);
+ if (team != null && team.hasPlayer(entity)) {
+ if (team.getSize() == 1) {
+ team.setPrefix("");
+ team.setSuffix("");
+ }
+ team.removePlayer(entity);
+ }
+ }
+ NMS.removeFromWorld(entity);
+ SkinnableEntity npc = entity instanceof SkinnableEntity ? (SkinnableEntity) entity : null;
+ npc.getSkinTracker().onRemoveNPC();
+ }
+ NMS.remove(entity);
+ // Paper decided to break Spigot compatibility.
+ // super.remove();
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/IllusionerController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/IllusionerController.java
new file mode 100644
index 000000000..e4dc48380
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/IllusionerController.java
@@ -0,0 +1,205 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftIllusioner;
+import org.bukkit.entity.Illusioner;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityIllagerIllusioner;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class IllusionerController extends MobEntityController {
+ public IllusionerController() {
+ super(EntityIllusionerNPC.class);
+ }
+
+ @Override
+ public Illusioner getBukkitEntity() {
+ return (Illusioner) super.getBukkitEntity();
+ }
+
+ public static class EntityIllusionerNPC extends EntityIllagerIllusioner implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityIllusionerNPC(EntityTypes extends EntityIllagerIllusioner> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityIllusionerNPC(EntityTypes extends EntityIllagerIllusioner> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new IllusionerNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+
+ public static class IllusionerNPC extends CraftIllusioner implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public IllusionerNPC(EntityIllusionerNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/IronGolemController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/IronGolemController.java
new file mode 100644
index 000000000..cc5658aa3
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/IronGolemController.java
@@ -0,0 +1,213 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftIronGolem;
+import org.bukkit.entity.IronGolem;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityIronGolem;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class IronGolemController extends MobEntityController {
+ public IronGolemController() {
+ super(EntityIronGolemNPC.class);
+ }
+
+ @Override
+ public IronGolem getBukkitEntity() {
+ return (IronGolem) super.getBukkitEntity();
+ }
+
+ public static class EntityIronGolemNPC extends EntityIronGolem implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityIronGolemNPC(EntityTypes extends EntityIronGolem> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityIronGolemNPC(EntityTypes extends EntityIronGolem> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new IronGolemNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+ }
+
+ public static class IronGolemNPC extends CraftIronGolem implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public IronGolemNPC(EntityIronGolemNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/LlamaController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/LlamaController.java
new file mode 100644
index 000000000..1fb17db9d
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/LlamaController.java
@@ -0,0 +1,231 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftLlama;
+import org.bukkit.entity.Llama;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.trait.HorseModifiers;
+import net.citizensnpcs.util.NMS;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityLlama;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class LlamaController extends MobEntityController {
+ public LlamaController() {
+ super(EntityLlamaNPC.class);
+ }
+
+ @Override
+ public Llama getBukkitEntity() {
+ return (Llama) super.getBukkitEntity();
+ }
+
+ @Override
+ public void spawn(Location at, NPC npc) {
+ npc.getTrait(HorseModifiers.class);
+ super.spawn(at, npc);
+ }
+
+ public static class EntityLlamaNPC extends EntityLlama implements NPCHolder {
+ boolean calledNMSHeight = false;
+
+ private final CitizensNPC npc;
+
+ public EntityLlamaNPC(EntityTypes extends EntityLlama> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityLlamaNPC(EntityTypes extends EntityLlama> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ ((Llama) getBukkitEntity()).setDomestication(((Llama) getBukkitEntity()).getMaxDomestication());
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new LlamaNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ if (npc == null) {
+ super.mobTick();
+ } else {
+ NMS.setStepHeight(getBukkitEntity(), 1);
+ npc.update();
+ }
+ }
+ }
+
+ public static class LlamaNPC extends CraftLlama implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public LlamaNPC(EntityLlamaNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/MagmaCubeController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/MagmaCubeController.java
new file mode 100644
index 000000000..d5339e06a
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/MagmaCubeController.java
@@ -0,0 +1,224 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftMagmaCube;
+import org.bukkit.entity.MagmaCube;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.nms.v1_15_R1.util.PlayerControllerMove;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityHuman;
+import net.minecraft.server.v1_15_R1.EntityMagmaCube;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class MagmaCubeController extends MobEntityController {
+ public MagmaCubeController() {
+ super(EntityMagmaCubeNPC.class);
+ }
+
+ @Override
+ public MagmaCube getBukkitEntity() {
+ return (MagmaCube) super.getBukkitEntity();
+ }
+
+ public static class EntityMagmaCubeNPC extends EntityMagmaCube implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityMagmaCubeNPC(EntityTypes extends EntityMagmaCube> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityMagmaCubeNPC(EntityTypes extends EntityMagmaCube> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ setSize(3, true);
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ this.moveController = new PlayerControllerMove(this);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new MagmaCubeNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void pickup(EntityHuman human) {
+ if (npc == null) {
+ super.pickup(human);
+ }
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+ }
+
+ public static class MagmaCubeNPC extends CraftMagmaCube implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public MagmaCubeNPC(EntityMagmaCubeNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/MobEntityController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/MobEntityController.java
new file mode 100644
index 000000000..49cd2a5f0
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/MobEntityController.java
@@ -0,0 +1,66 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import java.lang.reflect.Constructor;
+import java.util.Map;
+
+import org.bukkit.Location;
+import org.bukkit.block.BlockFace;
+import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
+import org.bukkit.entity.Entity;
+
+import com.google.common.collect.Maps;
+
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.AbstractEntityController;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.World;
+
+public abstract class MobEntityController extends AbstractEntityController {
+ private final Class> clazz;
+ private final Constructor> constructor;
+
+ protected MobEntityController(Class> clazz) {
+ super(clazz);
+ this.constructor = getConstructor(clazz);
+ this.clazz = clazz;
+ }
+
+ @Override
+ protected Entity createEntity(Location at, NPC npc) {
+ EntityTypes> type = NMSImpl.getEntityType(clazz);
+ net.minecraft.server.v1_15_R1.Entity entity = createEntityFromClass(type,
+ ((CraftWorld) at.getWorld()).getHandle(), npc);
+ entity.setPositionRotation(at.getX(), at.getY(), at.getZ(), at.getYaw(), at.getPitch());
+
+ // entity.onGround isn't updated right away - we approximate here so
+ // that things like pathfinding still work *immediately* after spawn.
+ org.bukkit.Material beneath = at.getBlock().getRelative(BlockFace.DOWN).getType();
+ if (beneath.isBlock()) {
+ entity.onGround = true;
+ }
+ return entity.getBukkitEntity();
+ }
+
+ private net.minecraft.server.v1_15_R1.Entity createEntityFromClass(Object... args) {
+ try {
+ return (net.minecraft.server.v1_15_R1.Entity) constructor.newInstance(args);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return null;
+ }
+ }
+
+ private static Constructor> getConstructor(Class> clazz) {
+ Constructor> constructor = CONSTRUCTOR_CACHE.get(clazz);
+ if (constructor != null)
+ return constructor;
+ try {
+ return clazz.getConstructor(EntityTypes.class, World.class, NPC.class);
+ } catch (Exception ex) {
+ throw new IllegalStateException("unable to find an entity constructor");
+ }
+ }
+
+ private static final Map, Constructor>> CONSTRUCTOR_CACHE = Maps.newHashMap();
+}
\ No newline at end of file
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/MushroomCowController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/MushroomCowController.java
new file mode 100644
index 000000000..e2304a3ae
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/MushroomCowController.java
@@ -0,0 +1,218 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftMushroomCow;
+import org.bukkit.entity.MushroomCow;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityMushroomCow;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class MushroomCowController extends MobEntityController {
+
+ public MushroomCowController() {
+ super(EntityMushroomCowNPC.class);
+ }
+
+ @Override
+ public MushroomCow getBukkitEntity() {
+ return (MushroomCow) super.getBukkitEntity();
+ }
+
+ public static class EntityMushroomCowNPC extends EntityMushroomCow implements NPCHolder {
+ boolean calledNMSHeight = false;
+
+ private final CitizensNPC npc;
+
+ public EntityMushroomCowNPC(EntityTypes extends EntityMushroomCow> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityMushroomCowNPC(EntityTypes extends EntityMushroomCow> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new MushroomCowNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null)
+ npc.update();
+ }
+ }
+
+ public static class MushroomCowNPC extends CraftMushroomCow implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public MushroomCowNPC(EntityMushroomCowNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/OcelotController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/OcelotController.java
new file mode 100644
index 000000000..a89822099
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/OcelotController.java
@@ -0,0 +1,218 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftOcelot;
+import org.bukkit.entity.Ocelot;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityOcelot;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class OcelotController extends MobEntityController {
+ public OcelotController() {
+ super(EntityOcelotNPC.class);
+ }
+
+ @Override
+ public Ocelot getBukkitEntity() {
+ return (Ocelot) super.getBukkitEntity();
+ }
+
+ public static class EntityOcelotNPC extends EntityOcelot implements NPCHolder {
+ boolean calledNMSHeight = false;
+ private final CitizensNPC npc;
+
+ public EntityOcelotNPC(EntityTypes extends EntityOcelot> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityOcelotNPC(EntityTypes extends EntityOcelot> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new OcelotNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+
+ public static class OcelotNPC extends CraftOcelot implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public OcelotNPC(EntityOcelotNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PandaController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PandaController.java
new file mode 100644
index 000000000..bd14851d3
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PandaController.java
@@ -0,0 +1,218 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPanda;
+import org.bukkit.entity.Panda;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityPanda;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class PandaController extends MobEntityController {
+ public PandaController() {
+ super(EntityPandaNPC.class);
+ }
+
+ @Override
+ public Panda getBukkitEntity() {
+ return (Panda) super.getBukkitEntity();
+ }
+
+ public static class EntityPandaNPC extends EntityPanda implements NPCHolder {
+ boolean calledNMSHeight = false;
+ private final CitizensNPC npc;
+
+ public EntityPandaNPC(EntityTypes extends EntityPanda> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityPandaNPC(EntityTypes extends EntityPanda> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new PandaNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+
+ public static class PandaNPC extends CraftPanda implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public PandaNPC(EntityPandaNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ParrotController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ParrotController.java
new file mode 100644
index 000000000..b26c08809
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ParrotController.java
@@ -0,0 +1,183 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftParrot;
+import org.bukkit.entity.Parrot;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityHuman;
+import net.minecraft.server.v1_15_R1.EntityParrot;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EnumHand;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.World;
+
+public class ParrotController extends MobEntityController {
+ public ParrotController() {
+ super(EntityParrotNPC.class);
+ }
+
+ @Override
+ public Parrot getBukkitEntity() {
+ return (Parrot) super.getBukkitEntity();
+ }
+
+ public static class EntityParrotNPC extends EntityParrot implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityParrotNPC(EntityTypes extends EntityParrot> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityParrotNPC(EntityTypes extends EntityParrot> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ public boolean a(EntityHuman paramEntityHuman, EnumHand paramEnumHand) {
+ // block feeding
+ if (npc == null || !npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true)) {
+ return super.a(paramEntityHuman, paramEnumHand);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new ParrotNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null) {
+ return super.isLeashed();
+ }
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ if (npc == null) {
+ super.mobTick();
+ } else {
+ NMSImpl.updateAI(this);
+ npc.update();
+ }
+ }
+ }
+
+ public static class ParrotNPC extends CraftParrot implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public ParrotNPC(EntityParrotNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PhantomController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PhantomController.java
new file mode 100644
index 000000000..2cf894829
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PhantomController.java
@@ -0,0 +1,234 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPhantom;
+import org.bukkit.entity.Phantom;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.ControllerLook;
+import net.minecraft.server.v1_15_R1.ControllerMove;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityPhantom;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EnumDifficulty;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class PhantomController extends MobEntityController {
+ public PhantomController() {
+ super(EntityPhantomNPC.class);
+ }
+
+ @Override
+ public Phantom getBukkitEntity() {
+ return (Phantom) super.getBukkitEntity();
+ }
+
+ public static class EntityPhantomNPC extends EntityPhantom implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityPhantomNPC(EntityTypes extends EntityPhantom> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityPhantomNPC(EntityTypes extends EntityPhantom> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ setNoAI(true);
+ this.moveController = new ControllerMove(this);
+ this.lookController = new ControllerLook(this);
+ // TODO: phantom pitch reversed
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public boolean en() {
+ if (npc == null || !npc.isProtected())
+ return super.en();
+ return false;
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new PhantomNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void movementTick() {
+ super.movementTick();
+ if (npc != null) {
+ if (npc.isProtected()) {
+ this.setOnFire(0);
+ }
+ npc.update();
+ }
+ }
+
+ @Override
+ public void tick() {
+ // avoid suicide
+ boolean resetDifficulty = this.world.getDifficulty() == EnumDifficulty.PEACEFUL;
+ if (npc != null && resetDifficulty) {
+ this.world.getWorldData().setDifficulty(EnumDifficulty.NORMAL);
+ }
+ super.tick();
+ if (npc != null && resetDifficulty) {
+ this.world.getWorldData().setDifficulty(EnumDifficulty.PEACEFUL);
+ }
+ }
+ }
+
+ public static class PhantomNPC extends CraftPhantom implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public PhantomNPC(EntityPhantomNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PigController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PigController.java
new file mode 100644
index 000000000..7acec09bc
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PigController.java
@@ -0,0 +1,232 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPig;
+import org.bukkit.entity.Pig;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityLightning;
+import net.minecraft.server.v1_15_R1.EntityPig;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class PigController extends MobEntityController {
+ public PigController() {
+ super(EntityPigNPC.class);
+ }
+
+ @Override
+ public Pig getBukkitEntity() {
+ return (Pig) super.getBukkitEntity();
+ }
+
+ public static class EntityPigNPC extends EntityPig implements NPCHolder {
+ boolean calledNMSHeight = false;
+ private final CitizensNPC npc;
+
+ public EntityPigNPC(EntityTypes extends EntityPig> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityPigNPC(EntityTypes extends EntityPig> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public boolean dY() {
+ // block carrot-on-a-stick behaviour
+ return npc == null ? super.dY() : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new PigNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void onLightningStrike(EntityLightning entitylightning) {
+ if (npc == null) {
+ super.onLightningStrike(entitylightning);
+ }
+ }
+ }
+
+ public static class PigNPC extends CraftPig implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public PigNPC(EntityPigNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PigZombieController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PigZombieController.java
new file mode 100644
index 000000000..02c9ca30d
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PigZombieController.java
@@ -0,0 +1,206 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPigZombie;
+import org.bukkit.entity.PigZombie;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityPigZombie;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class PigZombieController extends MobEntityController {
+
+ public PigZombieController() {
+ super(EntityPigZombieNPC.class);
+ }
+
+ @Override
+ public PigZombie getBukkitEntity() {
+ return (PigZombie) super.getBukkitEntity();
+ }
+
+ public static class EntityPigZombieNPC extends EntityPigZombie implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityPigZombieNPC(EntityTypes extends EntityPigZombie> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityPigZombieNPC(EntityTypes extends EntityPigZombie> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true)) {
+ super.f(x, y, z);
+ }
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new PigZombieNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+
+ public static class PigZombieNPC extends CraftPigZombie implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public PigZombieNPC(EntityPigZombieNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PillagerController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PillagerController.java
new file mode 100644
index 000000000..1434c1d33
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PillagerController.java
@@ -0,0 +1,218 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPillager;
+import org.bukkit.entity.Pillager;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityPillager;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class PillagerController extends MobEntityController {
+ public PillagerController() {
+ super(EntityPillagerNPC.class);
+ }
+
+ @Override
+ public Pillager getBukkitEntity() {
+ return (Pillager) super.getBukkitEntity();
+ }
+
+ public static class EntityPillagerNPC extends EntityPillager implements NPCHolder {
+ boolean calledNMSHeight = false;
+ private final CitizensNPC npc;
+
+ public EntityPillagerNPC(EntityTypes extends EntityPillager> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityPillagerNPC(EntityTypes extends EntityPillager> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new PillagerNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+
+ public static class PillagerNPC extends CraftPillager implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public PillagerNPC(EntityPillagerNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PolarBearController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PolarBearController.java
new file mode 100644
index 000000000..060c45eb4
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PolarBearController.java
@@ -0,0 +1,184 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPolarBear;
+import org.bukkit.entity.PolarBear;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityPolarBear;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.World;
+
+public class PolarBearController extends MobEntityController {
+ public PolarBearController() {
+ super(EntityPolarBearNPC.class);
+ }
+
+ @Override
+ public PolarBear getBukkitEntity() {
+ return (PolarBear) super.getBukkitEntity();
+ }
+
+ public static class EntityPolarBearNPC extends EntityPolarBear implements NPCHolder {
+ boolean calledNMSHeight = false;
+
+ private final CitizensNPC npc;
+
+ public EntityPolarBearNPC(EntityTypes extends EntityPolarBear> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityPolarBearNPC(EntityTypes extends EntityPolarBear> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new PolarBearNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null) {
+ return super.isLeashed();
+ }
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+
+ public static class PolarBearNPC extends CraftPolarBear implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public PolarBearNPC(EntityPolarBearNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PufferFishController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PufferFishController.java
new file mode 100644
index 000000000..c6e2fc1ef
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/PufferFishController.java
@@ -0,0 +1,233 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPufferFish;
+import org.bukkit.entity.PufferFish;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.ControllerMove;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityPufferFish;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class PufferFishController extends MobEntityController {
+ public PufferFishController() {
+ super(EntityPufferFishNPC.class);
+ }
+
+ @Override
+ public PufferFish getBukkitEntity() {
+ return (PufferFish) super.getBukkitEntity();
+ }
+
+ public static class EntityPufferFishNPC extends EntityPufferFish implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityPufferFishNPC(EntityTypes extends EntityPufferFish> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityPufferFishNPC(EntityTypes extends EntityPufferFish> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ this.moveController = new ControllerMove(this);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new PufferFishNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void movementTick() {
+ boolean lastInWater = this.v;
+ int lastPuffState = getPuffState();
+ if (npc != null) {
+ this.v = false;
+ setPuffState(0);
+ }
+ super.movementTick();
+ if (npc != null) {
+ this.v = lastInWater;
+ setPuffState(lastPuffState);
+ }
+ }
+
+ @Override
+ public void tick() {
+ int lastPuffState = getPuffState();
+ if (npc != null) {
+ NMSImpl.resetPuffTicks(this);
+ }
+ super.tick();
+ if (npc != null) {
+ setPuffState(lastPuffState);
+ }
+ }
+ }
+
+ public static class PufferFishNPC extends CraftPufferFish implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public PufferFishNPC(EntityPufferFishNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/RabbitController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/RabbitController.java
new file mode 100644
index 000000000..1fcf7b6f7
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/RabbitController.java
@@ -0,0 +1,242 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftRabbit;
+import org.bukkit.entity.Rabbit;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.NMS;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityLiving;
+import net.minecraft.server.v1_15_R1.EntityRabbit;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class RabbitController extends MobEntityController {
+ public RabbitController() {
+ super(EntityRabbitNPC.class);
+ }
+
+ @Override
+ public Rabbit getBukkitEntity() {
+ return (Rabbit) super.getBukkitEntity();
+ }
+
+ public static class EntityRabbitNPC extends EntityRabbit implements NPCHolder {
+ boolean calledNMSHeight = false;
+
+ private final CitizensNPC npc;
+
+ public EntityRabbitNPC(EntityTypes extends EntityRabbit> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityRabbitNPC(EntityTypes extends EntityRabbit> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new RabbitNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public EntityLiving getGoalTarget() {
+ return npc != null ? null : super.getGoalTarget();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ if (npc != null) {
+ super.mobTick();
+ if (npc.getNavigator().isNavigating()) {
+ NMS.setShouldJump(getBukkitEntity());
+ }
+ npc.update();
+ } else {
+ super.mobTick();
+ }
+ }
+
+ @Override
+ public void setRabbitType(int i) {
+ if (npc != null) {
+ if (NMSImpl.getRabbitTypeField() == null)
+ return;
+ this.datawatcher.set(NMSImpl.getRabbitTypeField(), i);
+ return;
+ }
+ super.setRabbitType(i);
+ }
+ }
+
+ public static class RabbitNPC extends CraftRabbit implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public RabbitNPC(EntityRabbitNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/RavagerController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/RavagerController.java
new file mode 100644
index 000000000..bcd69ad9b
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/RavagerController.java
@@ -0,0 +1,218 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftRavager;
+import org.bukkit.entity.Ravager;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityRavager;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class RavagerController extends MobEntityController {
+ public RavagerController() {
+ super(EntityRavagerNPC.class);
+ }
+
+ @Override
+ public Ravager getBukkitEntity() {
+ return (Ravager) super.getBukkitEntity();
+ }
+
+ public static class EntityRavagerNPC extends EntityRavager implements NPCHolder {
+ boolean calledNMSHeight = false;
+ private final CitizensNPC npc;
+
+ public EntityRavagerNPC(EntityTypes extends EntityRavager> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityRavagerNPC(EntityTypes extends EntityRavager> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new RavagerNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+
+ public static class RavagerNPC extends CraftRavager implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public RavagerNPC(EntityRavagerNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SalmonController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SalmonController.java
new file mode 100644
index 000000000..e17863c64
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SalmonController.java
@@ -0,0 +1,221 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftSalmon;
+import org.bukkit.entity.Salmon;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.ControllerMove;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntitySalmon;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class SalmonController extends MobEntityController {
+ public SalmonController() {
+ super(EntitySalmonNPC.class);
+ }
+
+ @Override
+ public Salmon getBukkitEntity() {
+ return (Salmon) super.getBukkitEntity();
+ }
+
+ public static class EntitySalmonNPC extends EntitySalmon implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntitySalmonNPC(EntityTypes extends EntitySalmon> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntitySalmonNPC(EntityTypes extends EntitySalmon> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ this.moveController = new ControllerMove(this);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new SalmonNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ if (npc != null) {
+ NMSImpl.setNotInSchool(this);
+ }
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void movementTick() {
+ boolean lastInWater = this.v;
+ if (npc != null) {
+ this.v = false;
+ }
+ super.movementTick();
+ if (npc != null) {
+ this.v = lastInWater;
+ }
+ }
+ }
+
+ public static class SalmonNPC extends CraftSalmon implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public SalmonNPC(EntitySalmonNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SheepController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SheepController.java
new file mode 100644
index 000000000..471dbc3c2
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SheepController.java
@@ -0,0 +1,217 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftSheep;
+import org.bukkit.entity.Sheep;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntitySheep;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class SheepController extends MobEntityController {
+ public SheepController() {
+ super(EntitySheepNPC.class);
+ }
+
+ @Override
+ public Sheep getBukkitEntity() {
+ return (Sheep) super.getBukkitEntity();
+ }
+
+ public static class EntitySheepNPC extends EntitySheep implements NPCHolder {
+ boolean calledNMSHeight = false;
+
+ private final CitizensNPC npc;
+
+ public EntitySheepNPC(EntityTypes extends EntitySheep> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntitySheepNPC(EntityTypes extends EntitySheep> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new SheepNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null)
+ npc.update();
+ }
+ }
+
+ public static class SheepNPC extends CraftSheep implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public SheepNPC(EntitySheepNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ShulkerController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ShulkerController.java
new file mode 100644
index 000000000..61d3e1089
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ShulkerController.java
@@ -0,0 +1,227 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftShulker;
+import org.bukkit.entity.Shulker;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityAIBodyControl;
+import net.minecraft.server.v1_15_R1.EntityShulker;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class ShulkerController extends MobEntityController {
+ public ShulkerController() {
+ super(EntityShulkerNPC.class);
+ }
+
+ @Override
+ public Shulker getBukkitEntity() {
+ return (Shulker) super.getBukkitEntity();
+ }
+
+ public static class EntityShulkerNPC extends EntityShulker implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityShulkerNPC(EntityTypes extends EntityShulker> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityShulkerNPC(EntityTypes extends EntityShulker> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new ShulkerNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void movementTick() {
+ if (npc == null) {
+ super.movementTick();
+ }
+ }
+
+ @Override
+ protected EntityAIBodyControl o() {
+ return new EntityAIBodyControl(this);
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+ }
+
+ public static class ShulkerNPC extends CraftShulker implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public ShulkerNPC(EntityShulkerNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SilverfishController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SilverfishController.java
new file mode 100644
index 000000000..4f34fe4f7
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SilverfishController.java
@@ -0,0 +1,210 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftSilverfish;
+import org.bukkit.entity.Silverfish;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntitySilverfish;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class SilverfishController extends MobEntityController {
+ public SilverfishController() {
+ super(EntitySilverfishNPC.class);
+ }
+
+ @Override
+ public Silverfish getBukkitEntity() {
+ return (Silverfish) super.getBukkitEntity();
+ }
+
+ public static class EntitySilverfishNPC extends EntitySilverfish implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntitySilverfishNPC(EntityTypes extends EntitySilverfish> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntitySilverfishNPC(EntityTypes extends EntitySilverfish> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null)
+ super.enderTeleportTo(d0, d1, d2);
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new SilverfishNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null)
+ npc.update();
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+ }
+
+ public static class SilverfishNPC extends CraftSilverfish implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public SilverfishNPC(EntitySilverfishNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SkeletonController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SkeletonController.java
new file mode 100644
index 000000000..60e290b8a
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SkeletonController.java
@@ -0,0 +1,213 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftSkeleton;
+import org.bukkit.entity.Skeleton;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntitySkeleton;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class SkeletonController extends MobEntityController {
+ public SkeletonController() {
+ super(EntitySkeletonNPC.class);
+ }
+
+ @Override
+ public Skeleton getBukkitEntity() {
+ return (Skeleton) super.getBukkitEntity();
+ }
+
+ public static class EntitySkeletonNPC extends EntitySkeleton implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntitySkeletonNPC(EntityTypes extends EntitySkeleton> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntitySkeletonNPC(EntityTypes extends EntitySkeleton> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new SkeletonNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+ }
+
+ public static class SkeletonNPC extends CraftSkeleton implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public SkeletonNPC(EntitySkeletonNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SkeletonStrayController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SkeletonStrayController.java
new file mode 100644
index 000000000..6a6533939
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SkeletonStrayController.java
@@ -0,0 +1,213 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftStray;
+import org.bukkit.entity.Stray;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntitySkeletonStray;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class SkeletonStrayController extends MobEntityController {
+ public SkeletonStrayController() {
+ super(EntityStrayNPC.class);
+ }
+
+ @Override
+ public Stray getBukkitEntity() {
+ return (Stray) super.getBukkitEntity();
+ }
+
+ public static class EntityStrayNPC extends EntitySkeletonStray implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityStrayNPC(EntityTypes extends EntitySkeletonStray> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityStrayNPC(EntityTypes extends EntitySkeletonStray> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new StrayNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+ }
+
+ public static class StrayNPC extends CraftStray implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public StrayNPC(EntityStrayNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SkeletonWitherController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SkeletonWitherController.java
new file mode 100644
index 000000000..f622179cd
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SkeletonWitherController.java
@@ -0,0 +1,213 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftWitherSkeleton;
+import org.bukkit.entity.WitherSkeleton;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntitySkeletonWither;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class SkeletonWitherController extends MobEntityController {
+ public SkeletonWitherController() {
+ super(EntitySkeletonWitherNPC.class);
+ }
+
+ @Override
+ public WitherSkeleton getBukkitEntity() {
+ return (WitherSkeleton) super.getBukkitEntity();
+ }
+
+ public static class EntitySkeletonWitherNPC extends EntitySkeletonWither implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntitySkeletonWitherNPC(EntityTypes extends EntitySkeletonWither> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntitySkeletonWitherNPC(EntityTypes extends EntitySkeletonWither> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new SkeletonWitherNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+ }
+
+ public static class SkeletonWitherNPC extends CraftWitherSkeleton implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public SkeletonWitherNPC(EntitySkeletonWitherNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SlimeController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SlimeController.java
new file mode 100644
index 000000000..dd99b2153
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SlimeController.java
@@ -0,0 +1,225 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftSlime;
+import org.bukkit.entity.Slime;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.nms.v1_15_R1.util.PlayerControllerMove;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityHuman;
+import net.minecraft.server.v1_15_R1.EntitySlime;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class SlimeController extends MobEntityController {
+ public SlimeController() {
+ super(EntitySlimeNPC.class);
+ }
+
+ @Override
+ public Slime getBukkitEntity() {
+ return (Slime) super.getBukkitEntity();
+ }
+
+ public static class EntitySlimeNPC extends EntitySlime implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntitySlimeNPC(EntityTypes extends EntitySlime> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntitySlimeNPC(EntityTypes extends EntitySlime> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ setSize(3, true);
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ this.moveController = new PlayerControllerMove(this);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new SlimeNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void pickup(EntityHuman human) {
+ if (npc == null) {
+ super.pickup(human);
+ }
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+ }
+
+ public static class SlimeNPC extends CraftSlime implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public SlimeNPC(EntitySlimeNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SnowmanController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SnowmanController.java
new file mode 100644
index 000000000..c5fb104dc
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SnowmanController.java
@@ -0,0 +1,226 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftSnowman;
+import org.bukkit.entity.Snowman;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntitySnowman;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.GameRules;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class SnowmanController extends MobEntityController {
+ public SnowmanController() {
+ super(EntitySnowmanNPC.class);
+ }
+
+ @Override
+ public Snowman getBukkitEntity() {
+ return (Snowman) super.getBukkitEntity();
+ }
+
+ public static class EntitySnowmanNPC extends EntitySnowman implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntitySnowmanNPC(EntityTypes extends EntitySnowman> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntitySnowmanNPC(EntityTypes extends EntitySnowman> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new SnowmanNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void movementTick() {
+ boolean allowsGriefing = this.world.getGameRules().get(GameRules.MOB_GRIEFING).a();
+ if (npc != null) {
+ this.world.getGameRules().get(GameRules.MOB_GRIEFING).setValue("false");
+ }
+ super.movementTick();
+ if (npc != null) {
+ this.world.getGameRules().get(GameRules.MOB_GRIEFING).setValue(Boolean.toString(allowsGriefing));
+ }
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+ }
+
+ public static class SnowmanNPC extends CraftSnowman implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public SnowmanNPC(EntitySnowmanNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SpiderController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SpiderController.java
new file mode 100644
index 000000000..b796199f7
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SpiderController.java
@@ -0,0 +1,213 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftSpider;
+import org.bukkit.entity.Spider;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntitySpider;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class SpiderController extends MobEntityController {
+ public SpiderController() {
+ super(EntitySpiderNPC.class);
+ }
+
+ @Override
+ public Spider getBukkitEntity() {
+ return (Spider) super.getBukkitEntity();
+ }
+
+ public static class EntitySpiderNPC extends EntitySpider implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntitySpiderNPC(EntityTypes extends EntitySpider> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntitySpiderNPC(EntityTypes extends EntitySpider> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new SpiderNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null)
+ npc.update();
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+
+ }
+
+ public static class SpiderNPC extends CraftSpider implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public SpiderNPC(EntitySpiderNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SquidController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SquidController.java
new file mode 100644
index 000000000..606fb81b0
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/SquidController.java
@@ -0,0 +1,205 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftSquid;
+import org.bukkit.entity.Squid;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntitySquid;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class SquidController extends MobEntityController {
+ public SquidController() {
+ super(EntitySquidNPC.class);
+ }
+
+ @Override
+ public Squid getBukkitEntity() {
+ return (Squid) super.getBukkitEntity();
+ }
+
+ public static class EntitySquidNPC extends EntitySquid implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntitySquidNPC(EntityTypes extends EntitySquid> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntitySquidNPC(EntityTypes extends EntitySquid> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new SquidNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+ }
+
+ public static class SquidNPC extends CraftSquid implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public SquidNPC(EntitySquidNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/TraderLlamaController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/TraderLlamaController.java
new file mode 100644
index 000000000..931e04a39
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/TraderLlamaController.java
@@ -0,0 +1,242 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import java.lang.invoke.MethodHandle;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftTraderLlama;
+import org.bukkit.entity.TraderLlama;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.trait.HorseModifiers;
+import net.citizensnpcs.util.NMS;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityLlamaTrader;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class TraderLlamaController extends MobEntityController {
+ public TraderLlamaController() {
+ super(EntityTraderLlamaNPC.class);
+ }
+
+ @Override
+ public TraderLlama getBukkitEntity() {
+ return (TraderLlama) super.getBukkitEntity();
+ }
+
+ @Override
+ public void spawn(Location at, NPC npc) {
+ npc.getTrait(HorseModifiers.class);
+ super.spawn(at, npc);
+ }
+
+ public static class EntityTraderLlamaNPC extends EntityLlamaTrader implements NPCHolder {
+ boolean calledNMSHeight = false;
+
+ private final CitizensNPC npc;
+
+ public EntityTraderLlamaNPC(EntityTypes extends EntityLlamaTrader> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityTraderLlamaNPC(EntityTypes extends EntityLlamaTrader> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ ((TraderLlama) getBukkitEntity())
+ .setDomestication(((TraderLlama) getBukkitEntity()).getMaxDomestication());
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new TraderLlamaNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ if (npc == null) {
+ super.mobTick();
+ } else {
+ try {
+ if (bJ != null) {
+ bJ.invoke(this, 10); // DespawnDelay
+ }
+ } catch (Throwable e) {
+ }
+ NMS.setStepHeight(getBukkitEntity(), 1);
+ npc.update();
+ }
+ }
+
+ private static final MethodHandle bJ = NMS.getSetter(EntityLlamaTrader.class, "bJ");
+ }
+
+ public static class TraderLlamaNPC extends CraftTraderLlama implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public TraderLlamaNPC(EntityTraderLlamaNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/TropicalFishController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/TropicalFishController.java
new file mode 100644
index 000000000..8904431d8
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/TropicalFishController.java
@@ -0,0 +1,221 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftTropicalFish;
+import org.bukkit.entity.TropicalFish;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.ControllerMove;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityTropicalFish;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class TropicalFishController extends MobEntityController {
+ public TropicalFishController() {
+ super(EntityTropicalFishNPC.class);
+ }
+
+ @Override
+ public TropicalFish getBukkitEntity() {
+ return (TropicalFish) super.getBukkitEntity();
+ }
+
+ public static class EntityTropicalFishNPC extends EntityTropicalFish implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityTropicalFishNPC(EntityTypes extends EntityTropicalFish> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityTropicalFishNPC(EntityTypes extends EntityTropicalFish> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ this.moveController = new ControllerMove(this);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new TropicalFishNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ if (npc != null) {
+ NMSImpl.setNotInSchool(this);
+ }
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void movementTick() {
+ boolean lastInWater = this.v;
+ if (npc != null) {
+ this.v = false;
+ }
+ super.movementTick();
+ if (npc != null) {
+ this.v = lastInWater;
+ }
+ }
+ }
+
+ public static class TropicalFishNPC extends CraftTropicalFish implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public TropicalFishNPC(EntityTropicalFishNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/TurtleController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/TurtleController.java
new file mode 100644
index 000000000..fee284cee
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/TurtleController.java
@@ -0,0 +1,221 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftTurtle;
+import org.bukkit.entity.Turtle;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.ControllerJump;
+import net.minecraft.server.v1_15_R1.ControllerMove;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityInsentient;
+import net.minecraft.server.v1_15_R1.EntityTurtle;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class TurtleController extends MobEntityController {
+ public TurtleController() {
+ super(EntityTurtleNPC.class);
+ }
+
+ @Override
+ public Turtle getBukkitEntity() {
+ return (Turtle) super.getBukkitEntity();
+ }
+
+ public static class EntityTurtleNPC extends EntityTurtle implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityTurtleNPC(EntityTypes extends EntityTurtle> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityTurtleNPC(EntityTypes extends EntityTurtle> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ this.moveController = new ControllerMove(this);
+ this.bq = new EmptyControllerJump(this);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new TurtleNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ static class EmptyControllerJump extends ControllerJump {
+
+ public EmptyControllerJump(EntityInsentient var1) {
+ super(var1);
+ }
+
+ @Override
+ public void b() {
+ this.a = false;
+ }
+ }
+ }
+
+ public static class TurtleNPC extends CraftTurtle implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public TurtleNPC(EntityTurtleNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/VexController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/VexController.java
new file mode 100644
index 000000000..e27f13b0a
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/VexController.java
@@ -0,0 +1,173 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import net.minecraft.server.v1_15_R1.Vec3D;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftVex;
+import org.bukkit.entity.Vex;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EntityVex;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.World;
+
+public class VexController extends MobEntityController {
+ public VexController() {
+ super(EntityVexNPC.class);
+ }
+
+ @Override
+ public Vex getBukkitEntity() {
+ return (Vex) super.getBukkitEntity();
+ }
+
+ public static class EntityVexNPC extends EntityVex implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityVexNPC(EntityTypes extends EntityVex> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityVexNPC(EntityTypes extends EntityVex> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ setNoGravity(true);
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new VexNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null) {
+ return super.isLeashed();
+ }
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+
+ public static class VexNPC extends CraftVex implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public VexNPC(EntityVexNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/VillagerController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/VillagerController.java
new file mode 100644
index 000000000..5bc6c7b75
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/VillagerController.java
@@ -0,0 +1,276 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import java.util.List;
+import java.util.TreeMap;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftVillager;
+import org.bukkit.entity.Villager;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BehaviorController;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityHuman;
+import net.minecraft.server.v1_15_R1.EntityLightning;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EntityVillager;
+import net.minecraft.server.v1_15_R1.EnumHand;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.MerchantRecipe;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class VillagerController extends MobEntityController {
+ public VillagerController() {
+ super(EntityVillagerNPC.class);
+ }
+
+ @Override
+ public Villager getBukkitEntity() {
+ return (Villager) super.getBukkitEntity();
+ }
+
+ public static class EntityVillagerNPC extends EntityVillager implements NPCHolder {
+ private TreeMap, ?> behaviorMap;
+ private boolean blockingATrade;
+ private boolean blockTrades = true;
+ boolean calledNMSHeight = false;
+ private final CitizensNPC npc;
+ private BehaviorController previousBehaviorController;
+
+ public EntityVillagerNPC(EntityTypes extends EntityVillager> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityVillagerNPC(EntityTypes extends EntityVillager> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean a(EntityHuman entityhuman, EnumHand enumhand) {
+ if (npc != null && blockTrades) {
+ blockingATrade = true;
+ List list = getOffers();
+ if (list != null) {
+ list.clear();
+ }
+ }
+ return super.a(entityhuman, enumhand);
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public boolean dY() {
+ if (blockingATrade) {
+ blockingATrade = false;
+ return true;
+ }
+ return super.dY();
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new VillagerNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ public boolean isBlockingTrades() {
+ return blockTrades;
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ if (npc != null) {
+ if (this.behaviorMap == null || this.previousBehaviorController != this.getBehaviorController()) {
+ this.behaviorMap = NMSImpl.getBehaviorMap(this);
+ this.previousBehaviorController = this.getBehaviorController();
+ }
+ if (this.behaviorMap.size() > 0) {
+ this.behaviorMap.clear();
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void onLightningStrike(EntityLightning entitylightning) {
+ if (npc == null) {
+ super.onLightningStrike(entitylightning);
+ }
+ }
+
+ public void setBlockTrades(boolean blocked) {
+ this.blockTrades = blocked;
+ }
+ }
+
+ public static class VillagerNPC extends CraftVillager implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public VillagerNPC(EntityVillagerNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/VindicatorController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/VindicatorController.java
new file mode 100644
index 000000000..8b9627f5a
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/VindicatorController.java
@@ -0,0 +1,219 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftVindicator;
+import org.bukkit.entity.Vindicator;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EntityVindicator;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class VindicatorController extends MobEntityController {
+ public VindicatorController() {
+ super(EntityVindicatorNPC.class);
+ }
+
+ @Override
+ public Vindicator getBukkitEntity() {
+ return (Vindicator) super.getBukkitEntity();
+ }
+
+ public static class EntityVindicatorNPC extends EntityVindicator implements NPCHolder {
+ boolean calledNMSHeight = false;
+
+ private final CitizensNPC npc;
+
+ public EntityVindicatorNPC(EntityTypes extends EntityVindicator> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityVindicatorNPC(EntityTypes extends EntityVindicator> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new VindicatorNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+
+ public static class VindicatorNPC extends CraftVindicator implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public VindicatorNPC(EntityVindicatorNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/WanderingTraderController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/WanderingTraderController.java
new file mode 100644
index 000000000..dff307042
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/WanderingTraderController.java
@@ -0,0 +1,272 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import java.util.List;
+import java.util.TreeMap;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftWanderingTrader;
+import org.bukkit.entity.WanderingTrader;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityHuman;
+import net.minecraft.server.v1_15_R1.EntityLightning;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EntityVillagerTrader;
+import net.minecraft.server.v1_15_R1.EnumHand;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.MerchantRecipe;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class WanderingTraderController extends MobEntityController {
+ public WanderingTraderController() {
+ super(EntityWanderingTraderNPC.class);
+ }
+
+ @Override
+ public WanderingTrader getBukkitEntity() {
+ return (WanderingTrader) super.getBukkitEntity();
+ }
+
+ public static class EntityWanderingTraderNPC extends EntityVillagerTrader implements NPCHolder {
+ private TreeMap, ?> behaviorMap;
+ private boolean blockingATrade;
+ private boolean blockTrades = true;
+ boolean calledNMSHeight = false;
+ private final CitizensNPC npc;
+
+ public EntityWanderingTraderNPC(EntityTypes extends EntityVillagerTrader> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityWanderingTraderNPC(EntityTypes extends EntityVillagerTrader> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean a(EntityHuman entityhuman, EnumHand enumhand) {
+ if (npc != null && blockTrades) {
+ blockingATrade = true;
+ List list = getOffers();
+ if (list != null) {
+ list.clear();
+ }
+ }
+ return super.a(entityhuman, enumhand);
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public boolean dY() {
+ if (blockingATrade) {
+ blockingATrade = false;
+ return true;
+ }
+ return super.dY();
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new WanderingTraderNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ public boolean isBlockingTrades() {
+ return blockTrades;
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ if (npc != null) {
+ if (this.behaviorMap == null) {
+ this.behaviorMap = NMSImpl.getBehaviorMap(this);
+ }
+ if (this.behaviorMap.size() > 0) {
+ this.behaviorMap.clear();
+ }
+ }
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public void onLightningStrike(EntityLightning entitylightning) {
+ if (npc == null) {
+ super.onLightningStrike(entitylightning);
+ }
+ }
+
+ public void setBlockTrades(boolean blocked) {
+ this.blockTrades = blocked;
+ }
+ }
+
+ public static class WanderingTraderNPC extends CraftWanderingTrader implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public WanderingTraderNPC(EntityWanderingTraderNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/WitchController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/WitchController.java
new file mode 100644
index 000000000..507e950dc
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/WitchController.java
@@ -0,0 +1,212 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftWitch;
+import org.bukkit.entity.Witch;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EntityWitch;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class WitchController extends MobEntityController {
+ public WitchController() {
+ super(EntityWitchNPC.class);
+ }
+
+ @Override
+ public Witch getBukkitEntity() {
+ return (Witch) super.getBukkitEntity();
+ }
+
+ public static class EntityWitchNPC extends EntityWitch implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityWitchNPC(EntityTypes extends EntityWitch> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityWitchNPC(EntityTypes extends EntityWitch> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new WitchNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null)
+ npc.update();
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+ }
+
+ public static class WitchNPC extends CraftWitch implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public WitchNPC(EntityWitchNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/WitherController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/WitherController.java
new file mode 100644
index 000000000..8be3aebb7
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/WitherController.java
@@ -0,0 +1,176 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import net.minecraft.server.v1_15_R1.Vec3D;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftWither;
+import org.bukkit.entity.Wither;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EntityWither;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.World;
+
+public class WitherController extends MobEntityController {
+ public WitherController() {
+ super(EntityWitherNPC.class);
+ }
+
+ @Override
+ public Wither getBukkitEntity() {
+ return (Wither) super.getBukkitEntity();
+ }
+
+ public static class EntityWitherNPC extends EntityWither implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityWitherNPC(EntityTypes extends EntityWither> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityWitherNPC(EntityTypes extends EntityWither> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new WitherNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public int l(int i) {
+ return npc == null ? super.l(i) : 0;
+ }
+
+ @Override
+ protected void mobTick() {
+ if (npc == null) {
+ super.mobTick();
+ } else {
+ npc.update();
+ }
+ }
+ }
+
+ public static class WitherNPC extends CraftWither implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public WitherNPC(EntityWitherNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/WolfController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/WolfController.java
new file mode 100644
index 000000000..a0f82bfcb
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/WolfController.java
@@ -0,0 +1,231 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftWolf;
+import org.bukkit.entity.Wolf;
+import org.bukkit.event.entity.EntityTargetEvent;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.DataWatcherObject;
+import net.minecraft.server.v1_15_R1.EntityLiving;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EntityWolf;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class WolfController extends MobEntityController {
+ public WolfController() {
+ super(EntityWolfNPC.class);
+ }
+
+ @Override
+ public Wolf getBukkitEntity() {
+ return (Wolf) super.getBukkitEntity();
+ }
+
+ public static class EntityWolfNPC extends EntityWolf implements NPCHolder {
+ boolean calledNMSHeight = false;
+
+ private final CitizensNPC npc;
+
+ public EntityWolfNPC(EntityTypes extends EntityWolf> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityWolfNPC(EntityTypes extends EntityWolf> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ public void a(DataWatcherObject> datawatcherobject) {
+ if (npc != null && !calledNMSHeight) {
+ calledNMSHeight = true;
+ NMSImpl.checkAndUpdateHeight(this, datawatcherobject);
+ calledNMSHeight = false;
+ }
+
+ super.a(datawatcherobject);
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new WolfNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+
+ @Override
+ public boolean setGoalTarget(EntityLiving entityliving, EntityTargetEvent.TargetReason reason, boolean fire) {
+ return npc == null || this.equals(entityliving) ? super.setGoalTarget(entityliving, reason, fire) : false;
+ }
+ }
+
+ public static class WolfNPC extends CraftWolf implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public WolfNPC(EntityWolfNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void setSitting(boolean sitting) {
+ getHandle().setSitting(sitting);
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ZombieController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ZombieController.java
new file mode 100644
index 000000000..7105ab859
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ZombieController.java
@@ -0,0 +1,204 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftZombie;
+import org.bukkit.entity.Zombie;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EntityZombie;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class ZombieController extends MobEntityController {
+ public ZombieController() {
+ super(EntityZombieNPC.class);
+ }
+
+ @Override
+ public Zombie getBukkitEntity() {
+ return (Zombie) super.getBukkitEntity();
+ }
+
+ public static class EntityZombieNPC extends EntityZombie implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityZombieNPC(EntityTypes extends EntityZombie> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityZombieNPC(EntityTypes extends EntityZombie> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new ZombieNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+
+ public static class ZombieNPC extends CraftZombie implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public ZombieNPC(EntityZombieNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ZombieHuskController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ZombieHuskController.java
new file mode 100644
index 000000000..ab26d1ef0
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ZombieHuskController.java
@@ -0,0 +1,204 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftHusk;
+import org.bukkit.entity.Husk;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EntityZombieHusk;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class ZombieHuskController extends MobEntityController {
+ public ZombieHuskController() {
+ super(EntityZombieHuskNPC.class);
+ }
+
+ @Override
+ public Husk getBukkitEntity() {
+ return (Husk) super.getBukkitEntity();
+ }
+
+ public static class EntityZombieHuskNPC extends EntityZombieHusk implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityZombieHuskNPC(EntityTypes extends EntityZombieHusk> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityZombieHuskNPC(EntityTypes extends EntityZombieHusk> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new ZombieHuskNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ protected SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ protected SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+
+ public static class ZombieHuskNPC extends CraftHusk implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public ZombieHuskNPC(EntityZombieHuskNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ZombieVillagerController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ZombieVillagerController.java
new file mode 100644
index 000000000..12fbd298f
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ZombieVillagerController.java
@@ -0,0 +1,204 @@
+package net.citizensnpcs.nms.v1_15_R1.entity;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftVillagerZombie;
+import org.bukkit.entity.ZombieVillager;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCEnderTeleportEvent;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EntityZombieVillager;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class ZombieVillagerController extends MobEntityController {
+ public ZombieVillagerController() {
+ super(EntityZombieVillagerNPC.class);
+ }
+
+ @Override
+ public ZombieVillager getBukkitEntity() {
+ return (ZombieVillager) super.getBukkitEntity();
+ }
+
+ public static class EntityZombieVillagerNPC extends EntityZombieVillager implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityZombieVillagerNPC(EntityTypes extends EntityZombieVillager> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityZombieVillagerNPC(EntityTypes extends EntityZombieVillager> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ if (npc != null) {
+ NMSImpl.clearGoals(goalSelector, targetSelector);
+ }
+ }
+
+ @Override
+ protected void a(double d0, boolean flag, IBlockData block, BlockPosition blockposition) {
+ if (npc == null || !npc.isFlyable()) {
+ super.a(d0, flag, block, blockposition);
+ }
+ }
+
+ @Override
+ public boolean b(float f, float f1) {
+ if (npc == null || !npc.isFlyable()) {
+ return super.b(f, f1);
+ }
+ return false;
+ }
+
+ @Override
+ public void checkDespawn() {
+ if (npc == null) {
+ super.checkDespawn();
+ }
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null)
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void e(Vec3D vec3d) {
+ if (npc == null || !npc.isFlyable()) {
+ super.e(vec3d);
+ } else {
+ NMSImpl.flyingMoveLogic(this, vec3d);
+ }
+ }
+
+ @Override
+ public void enderTeleportTo(double d0, double d1, double d2) {
+ if (npc == null) {
+ super.enderTeleportTo(d0, d1, d2);
+ return;
+ }
+ NPCEnderTeleportEvent event = new NPCEnderTeleportEvent(npc);
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ super.enderTeleportTo(d0, d1, d2);
+ }
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new ZombieVillagerNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public SoundEffect getSoundAmbient() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundAmbient(), NPC.AMBIENT_SOUND_METADATA);
+ }
+
+ @Override
+ public SoundEffect getSoundDeath() {
+ return NMSImpl.getSoundEffect(npc, super.getSoundDeath(), NPC.DEATH_SOUND_METADATA);
+ }
+
+ @Override
+ public SoundEffect getSoundHurt(DamageSource damagesource) {
+ return NMSImpl.getSoundEffect(npc, super.getSoundHurt(damagesource), NPC.HURT_SOUND_METADATA);
+ }
+
+ @Override
+ public boolean isClimbing() {
+ if (npc == null || !npc.isFlyable()) {
+ return super.isClimbing();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeashed() {
+ if (npc == null)
+ return super.isLeashed();
+ boolean protectedDefault = npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true);
+ if (!protectedDefault || !npc.data().get(NPC.LEASH_PROTECTED_METADATA, protectedDefault))
+ return super.isLeashed();
+ if (super.isLeashed()) {
+ unleash(true, false); // clearLeash with client update
+ }
+ return false; // shouldLeash
+ }
+
+ @Override
+ public void mobTick() {
+ super.mobTick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+
+ public static class ZombieVillagerNPC extends CraftVillagerZombie implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public ZombieVillagerNPC(EntityZombieVillagerNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/AreaEffectCloudController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/AreaEffectCloudController.java
new file mode 100644
index 000000000..958f23bec
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/AreaEffectCloudController.java
@@ -0,0 +1,118 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftAreaEffectCloud;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.AreaEffectCloud;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityAreaEffectCloud;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class AreaEffectCloudController extends MobEntityController {
+ public AreaEffectCloudController() {
+ super(EntityAreaEffectCloudNPC.class);
+ }
+
+ @Override
+ public AreaEffectCloud getBukkitEntity() {
+ return (AreaEffectCloud) super.getBukkitEntity();
+ }
+
+ public static class AreaEffectCloudNPC extends CraftAreaEffectCloud implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public AreaEffectCloudNPC(EntityAreaEffectCloudNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityAreaEffectCloudNPC extends EntityAreaEffectCloud implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityAreaEffectCloudNPC(EntityTypes extends EntityAreaEffectCloud> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityAreaEffectCloudNPC(EntityTypes extends EntityAreaEffectCloud> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new AreaEffectCloudNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ArmorStandController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ArmorStandController.java
new file mode 100644
index 000000000..b9eba012b
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ArmorStandController.java
@@ -0,0 +1,134 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftArmorStand;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.ArmorStand;
+import org.bukkit.entity.Player;
+import org.bukkit.event.player.PlayerInteractEntityEvent;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityArmorStand;
+import net.minecraft.server.v1_15_R1.EntityHuman;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EnumHand;
+import net.minecraft.server.v1_15_R1.EnumInteractionResult;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class ArmorStandController extends MobEntityController {
+ public ArmorStandController() {
+ super(EntityArmorStandNPC.class);
+ }
+
+ @Override
+ public ArmorStand getBukkitEntity() {
+ return (ArmorStand) super.getBukkitEntity();
+ }
+
+ public static class ArmorStandNPC extends CraftArmorStand implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public ArmorStandNPC(EntityArmorStandNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityArmorStandNPC extends EntityArmorStand implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityArmorStandNPC(EntityTypes extends EntityArmorStand> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityArmorStandNPC(EntityTypes extends EntityArmorStand> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public EnumInteractionResult a(EntityHuman entityhuman, Vec3D vec3d, EnumHand enumhand) {
+ if (npc == null) {
+ return super.a(entityhuman, vec3d, enumhand);
+ }
+ PlayerInteractEntityEvent event = new PlayerInteractEntityEvent((Player) entityhuman.getBukkitEntity(),
+ getBukkitEntity());
+ Bukkit.getPluginManager().callEvent(event);
+ return event.isCancelled() ? EnumInteractionResult.FAIL : EnumInteractionResult.SUCCESS;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new ArmorStandNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/BoatController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/BoatController.java
new file mode 100644
index 000000000..a7d885bc7
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/BoatController.java
@@ -0,0 +1,126 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftBoat;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.Boat;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityBoat;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class BoatController extends MobEntityController {
+ public BoatController() {
+ super(EntityBoatNPC.class);
+ }
+
+ @Override
+ public Boat getBukkitEntity() {
+ return (Boat) super.getBukkitEntity();
+ }
+
+ public static class BoatNPC extends CraftBoat implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public BoatNPC(EntityBoatNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityBoatNPC extends EntityBoat implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityBoatNPC(EntityTypes extends EntityBoat> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityBoatNPC(EntityTypes extends EntityBoat> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new BoatNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/DragonFireballController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/DragonFireballController.java
new file mode 100644
index 000000000..4a9099bc6
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/DragonFireballController.java
@@ -0,0 +1,129 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftDragonFireball;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.DragonFireball;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityDragonFireball;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class DragonFireballController extends MobEntityController {
+ public DragonFireballController() {
+ super(EntityDragonFireballNPC.class);
+ }
+
+ @Override
+ public DragonFireball getBukkitEntity() {
+ return (DragonFireball) super.getBukkitEntity();
+ }
+
+ public static class DragonFireballNPC extends CraftDragonFireball implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public DragonFireballNPC(EntityDragonFireballNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityDragonFireballNPC extends EntityDragonFireball implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityDragonFireballNPC(EntityTypes extends EntityDragonFireball> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityDragonFireballNPC(EntityTypes extends EntityDragonFireball> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new DragonFireballNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true)) {
+ super.tick();
+ }
+ } else {
+ super.tick();
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/EggController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/EggController.java
new file mode 100644
index 000000000..99c238146
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/EggController.java
@@ -0,0 +1,136 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEgg;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.Egg;
+import org.bukkit.entity.Entity;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.AbstractEntityController;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityEgg;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+import net.minecraft.server.v1_15_R1.WorldServer;
+
+public class EggController extends AbstractEntityController {
+ public EggController() {
+ super(EntityEggNPC.class);
+ }
+
+ @Override
+ protected Entity createEntity(Location at, NPC npc) {
+ WorldServer ws = ((CraftWorld) at.getWorld()).getHandle();
+ final EntityEggNPC handle = new EntityEggNPC(ws, npc, at.getX(), at.getY(), at.getZ());
+ return handle.getBukkitEntity();
+ }
+
+ @Override
+ public Egg getBukkitEntity() {
+ return (Egg) super.getBukkitEntity();
+ }
+
+ public static class EggNPC extends CraftEgg implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EggNPC(EntityEggNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityEggNPC extends EntityEgg implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityEggNPC(EntityTypes extends EntityEgg> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityEggNPC(EntityTypes extends EntityEgg> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ public EntityEggNPC(World world, NPC npc, double d0, double d1, double d2) {
+ super(world, d0, d1, d2);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new EggNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true)) {
+ super.tick();
+ }
+ } else {
+ super.tick();
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/EnderCrystalController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/EnderCrystalController.java
new file mode 100644
index 000000000..d6e5f27ff
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/EnderCrystalController.java
@@ -0,0 +1,118 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEnderCrystal;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.EnderCrystal;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityEnderCrystal;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class EnderCrystalController extends MobEntityController {
+ public EnderCrystalController() {
+ super(EntityEnderCrystalNPC.class);
+ }
+
+ @Override
+ public EnderCrystal getBukkitEntity() {
+ return (EnderCrystal) super.getBukkitEntity();
+ }
+
+ public static class EnderCrystalNPC extends CraftEnderCrystal implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EnderCrystalNPC(EntityEnderCrystalNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityEnderCrystalNPC extends EntityEnderCrystal implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityEnderCrystalNPC(EntityTypes extends EntityEnderCrystal> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityEnderCrystalNPC(EntityTypes extends EntityEnderCrystal> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new EnderCrystalNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/EnderPearlController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/EnderPearlController.java
new file mode 100644
index 000000000..e8ee4b464
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/EnderPearlController.java
@@ -0,0 +1,121 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEnderPearl;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.EnderPearl;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityEnderPearl;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class EnderPearlController extends MobEntityController {
+ public EnderPearlController() {
+ super(EntityEnderPearlNPC.class);
+ }
+
+ @Override
+ public EnderPearl getBukkitEntity() {
+ return (EnderPearl) super.getBukkitEntity();
+ }
+
+ public static class EnderPearlNPC extends CraftEnderPearl implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EnderPearlNPC(EntityEnderPearlNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityEnderPearlNPC extends EntityEnderPearl implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityEnderPearlNPC(EntityTypes extends EntityEnderPearl> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityEnderPearlNPC(EntityTypes extends EntityEnderPearl> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new EnderPearlNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true)) {
+ super.tick();
+ }
+ } else {
+ super.tick();
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/EnderSignalController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/EnderSignalController.java
new file mode 100644
index 000000000..84c2fdd95
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/EnderSignalController.java
@@ -0,0 +1,118 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEnderSignal;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.EnderSignal;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityEnderSignal;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class EnderSignalController extends MobEntityController {
+ public EnderSignalController() {
+ super(EntityEnderSignalNPC.class);
+ }
+
+ @Override
+ public EnderSignal getBukkitEntity() {
+ return (EnderSignal) super.getBukkitEntity();
+ }
+
+ public static class EnderSignalNPC extends CraftEnderSignal implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EnderSignalNPC(EntityEnderSignalNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class EntityEnderSignalNPC extends EntityEnderSignal implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityEnderSignalNPC(EntityTypes extends EntityEnderSignal> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityEnderSignalNPC(EntityTypes extends EntityEnderSignal> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new EnderSignalNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/EvokerFangsController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/EvokerFangsController.java
new file mode 100644
index 000000000..92a7d381f
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/EvokerFangsController.java
@@ -0,0 +1,134 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEvokerFangs;
+import org.bukkit.entity.EvokerFangs;
+import org.bukkit.entity.Player;
+import org.bukkit.event.player.PlayerInteractEntityEvent;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityEvokerFangs;
+import net.minecraft.server.v1_15_R1.EntityHuman;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EnumHand;
+import net.minecraft.server.v1_15_R1.EnumInteractionResult;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+
+public class EvokerFangsController extends MobEntityController {
+ public EvokerFangsController() {
+ super(EntityEvokerFangsNPC.class);
+ }
+
+ @Override
+ public EvokerFangs getBukkitEntity() {
+ return (EvokerFangs) super.getBukkitEntity();
+ }
+
+ public static class EntityEvokerFangsNPC extends EntityEvokerFangs implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityEvokerFangsNPC(EntityTypes extends EntityEvokerFangs> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityEvokerFangsNPC(EntityTypes extends EntityEvokerFangs> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public EnumInteractionResult a(EntityHuman entityhuman, Vec3D vec3d, EnumHand enumhand) {
+ if (npc == null) {
+ return super.a(entityhuman, vec3d, enumhand);
+ }
+ PlayerInteractEntityEvent event = new PlayerInteractEntityEvent((Player) entityhuman.getBukkitEntity(),
+ getBukkitEntity());
+ Bukkit.getPluginManager().callEvent(event);
+ return event.isCancelled() ? EnumInteractionResult.FAIL : EnumInteractionResult.SUCCESS;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new EvokerFangsNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+ if (npc != null) {
+ npc.update();
+ }
+ }
+ }
+
+ public static class EvokerFangsNPC extends CraftEvokerFangs implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EvokerFangsNPC(EntityEvokerFangsNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ExperienceOrbController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ExperienceOrbController.java
new file mode 100644
index 000000000..dff59e0f2
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ExperienceOrbController.java
@@ -0,0 +1,118 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftExperienceOrb;
+import org.bukkit.entity.ExperienceOrb;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityExperienceOrb;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class ExperienceOrbController extends MobEntityController {
+ public ExperienceOrbController() {
+ super(EntityExperienceOrbNPC.class);
+ }
+
+ @Override
+ public ExperienceOrb getBukkitEntity() {
+ return (ExperienceOrb) super.getBukkitEntity();
+ }
+
+ public static class EntityExperienceOrbNPC extends EntityExperienceOrb implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityExperienceOrbNPC(EntityTypes extends EntityExperienceOrb> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityExperienceOrbNPC(EntityTypes extends EntityExperienceOrb> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new ExperienceOrbNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class ExperienceOrbNPC extends CraftExperienceOrb implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public ExperienceOrbNPC(EntityExperienceOrbNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/FallingBlockController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/FallingBlockController.java
new file mode 100644
index 000000000..464f11dcb
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/FallingBlockController.java
@@ -0,0 +1,177 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftFallingBlock;
+import org.bukkit.craftbukkit.v1_15_R1.util.CraftMagicNumbers;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.FallingBlock;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.DespawnReason;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.event.SpawnReason;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.AbstractEntityController;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.Block;
+import net.minecraft.server.v1_15_R1.Blocks;
+import net.minecraft.server.v1_15_R1.EntityFallingBlock;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EnumMoveType;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.Vec3D;
+import net.minecraft.server.v1_15_R1.World;
+import net.minecraft.server.v1_15_R1.WorldServer;
+
+public class FallingBlockController extends AbstractEntityController {
+ public FallingBlockController() {
+ super(EntityFallingBlockNPC.class);
+ }
+
+ @Override
+ protected Entity createEntity(Location at, NPC npc) {
+ WorldServer ws = ((CraftWorld) at.getWorld()).getHandle();
+ Block id = Blocks.STONE;
+ int data = npc.data().get(NPC.ITEM_DATA_METADATA, npc.data().get("falling-block-data", 0));
+ // TODO: how to incorporate this - probably delete?
+ if (npc.data().has("falling-block-id") || npc.data().has(NPC.ITEM_ID_METADATA)) {
+ id = CraftMagicNumbers.getBlock(Material.getMaterial(
+ npc.data(). get(NPC.ITEM_ID_METADATA, npc.data(). get("falling-block-id"))));
+ }
+ final EntityFallingBlockNPC handle = new EntityFallingBlockNPC(ws, npc, at.getX(), at.getY(), at.getZ(),
+ id.getBlockData());
+ return handle.getBukkitEntity();
+ }
+
+ @Override
+ public FallingBlock getBukkitEntity() {
+ return (FallingBlock) super.getBukkitEntity();
+ }
+
+ public static class EntityFallingBlockNPC extends EntityFallingBlock implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityFallingBlockNPC(EntityTypes extends EntityFallingBlock> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityFallingBlockNPC(EntityTypes extends EntityFallingBlock> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ public EntityFallingBlockNPC(World world, NPC npc, double d0, double d1, double d2, IBlockData data) {
+ super(world, d0, d1, d2, data);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new FallingBlockNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ Vec3D mot = getMot();
+ if (Math.abs(mot.getX()) > EPSILON || Math.abs(mot.getY()) > EPSILON
+ || Math.abs(mot.getZ()) > EPSILON) {
+ mot = mot.d(0.98, 0.98, 0.98);
+ setMot(mot);
+ move(EnumMoveType.SELF, mot);
+ }
+ } else {
+ super.tick();
+ }
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+
+ private static final double EPSILON = 0.001;
+ }
+
+ public static class FallingBlockNPC extends CraftFallingBlock implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public FallingBlockNPC(EntityFallingBlockNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ public void setType(Material material, int data) {
+ npc.data().setPersistent(NPC.ITEM_ID_METADATA, material.name());
+ npc.data().setPersistent(NPC.ITEM_DATA_METADATA, data);
+ if (npc.isSpawned()) {
+ npc.despawn(DespawnReason.PENDING_RESPAWN);
+ npc.spawn(npc.getStoredLocation(), SpawnReason.RESPAWN);
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/FireworkController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/FireworkController.java
new file mode 100644
index 000000000..ff6da08da
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/FireworkController.java
@@ -0,0 +1,118 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftFirework;
+import org.bukkit.entity.Firework;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityFireworks;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class FireworkController extends MobEntityController {
+ public FireworkController() {
+ super(EntityFireworkNPC.class);
+ }
+
+ @Override
+ public Firework getBukkitEntity() {
+ return (Firework) super.getBukkitEntity();
+ }
+
+ public static class EntityFireworkNPC extends EntityFireworks implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityFireworkNPC(EntityTypes extends EntityFireworks> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityFireworkNPC(EntityTypes extends EntityFireworks> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new FireworkNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class FireworkNPC extends CraftFirework implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public FireworkNPC(EntityFireworkNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/FishingHookController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/FishingHookController.java
new file mode 100644
index 000000000..c45764ae3
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/FishingHookController.java
@@ -0,0 +1,152 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import java.util.UUID;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftFishHook;
+import org.bukkit.entity.FishHook;
+import org.bukkit.util.Vector;
+
+import com.mojang.authlib.GameProfile;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.NMS;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.Entity;
+import net.minecraft.server.v1_15_R1.EntityFishingHook;
+import net.minecraft.server.v1_15_R1.EntityPlayer;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.ItemStack;
+import net.minecraft.server.v1_15_R1.Items;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.PlayerInteractManager;
+import net.minecraft.server.v1_15_R1.World;
+import net.minecraft.server.v1_15_R1.WorldServer;
+
+public class FishingHookController extends MobEntityController {
+ public FishingHookController() {
+ super(EntityFishingHookNPC.class);
+ }
+
+ @Override
+ public FishHook getBukkitEntity() {
+ return (FishHook) super.getBukkitEntity();
+ }
+
+ public static class EntityFishingHookNPC extends EntityFishingHook implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityFishingHookNPC(EntityTypes extends EntityFishingHook> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityFishingHookNPC(EntityTypes extends EntityFishingHook> types, World world, NPC npc) {
+ super(new EntityPlayer(world.getServer().getServer(), (WorldServer) world,
+ new GameProfile(UUID.randomUUID(), "dummyfishhook"),
+ new PlayerInteractManager((WorldServer) world)) {
+ }, world, 0, 0);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new FishingHookNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public double h(Entity entity) {
+ // distance check in k()
+ if (entity == this.owner) {
+ return 0D;
+ }
+ return super.h(entity);
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ this.owner.setHealth(20F);
+ this.owner.dead = false;
+ this.owner.inventory.items.set(this.owner.inventory.itemInHandIndex,
+ new ItemStack(Items.FISHING_ROD, 1));
+ try {
+ NMS.getField(EntityFishingHook.class, "e").set(this, 0);
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class FishingHookNPC extends CraftFishHook implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public FishingHookNPC(EntityFishingHookNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ItemController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ItemController.java
new file mode 100644
index 000000000..31fdb7def
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ItemController.java
@@ -0,0 +1,157 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftItem;
+import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Item;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.DespawnReason;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.event.SpawnReason;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.AbstractEntityController;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityHuman;
+import net.minecraft.server.v1_15_R1.EntityItem;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.ItemStack;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+import net.minecraft.server.v1_15_R1.WorldServer;
+
+public class ItemController extends AbstractEntityController {
+ public ItemController() {
+ super(EntityItemNPC.class);
+ }
+
+ @Override
+ protected Entity createEntity(Location at, NPC npc) {
+ WorldServer ws = ((CraftWorld) at.getWorld()).getHandle();
+ Material id = Material.STONE;
+ int data = npc.data().get(NPC.ITEM_DATA_METADATA, npc.data().get("falling-block-data", 0));
+ if (npc.data().has(NPC.ITEM_ID_METADATA)) {
+ id = Material.getMaterial(npc.data(). get(NPC.ITEM_ID_METADATA));
+ }
+ final EntityItemNPC handle = new EntityItemNPC(ws, npc, at.getX(), at.getY(), at.getZ(),
+ CraftItemStack.asNMSCopy(new org.bukkit.inventory.ItemStack(id, 1, (short) data)));
+ return handle.getBukkitEntity();
+ }
+
+ @Override
+ public Item getBukkitEntity() {
+ return (Item) super.getBukkitEntity();
+ }
+
+ public static class EntityItemNPC extends EntityItem implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityItemNPC(EntityTypes extends EntityItem> types, World world) {
+ super(types, world);
+ this.npc = null;
+ }
+
+ public EntityItemNPC(World world, NPC npc, double x, double y, double z, ItemStack stack) {
+ super(world, x, y, z, stack);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new ItemNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void pickup(EntityHuman entityhuman) {
+ if (npc == null) {
+ super.pickup(entityhuman);
+ }
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class ItemNPC extends CraftItem implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public ItemNPC(EntityItemNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ public void setType(Material material, int data) {
+ npc.data().setPersistent(NPC.ITEM_ID_METADATA, material.name());
+ npc.data().setPersistent(NPC.ITEM_DATA_METADATA, data);
+ if (npc.isSpawned()) {
+ npc.despawn(DespawnReason.PENDING_RESPAWN);
+ npc.spawn(npc.getStoredLocation(), SpawnReason.RESPAWN);
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ItemFrameController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ItemFrameController.java
new file mode 100644
index 000000000..1b4be02af
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ItemFrameController.java
@@ -0,0 +1,155 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftItemFrame;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.ItemFrame;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.DespawnReason;
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.event.SpawnReason;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.EntityItemFrame;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EnumDirection;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class ItemFrameController extends MobEntityController {
+ public ItemFrameController() {
+ super(EntityItemFrameNPC.class);
+ }
+
+ @Override
+ protected Entity createEntity(Location at, NPC npc) {
+ Entity e = super.createEntity(at, npc);
+ EntityItemFrame item = (EntityItemFrame) ((CraftEntity) e).getHandle();
+ item.setDirection(EnumDirection.EAST);
+ item.blockPosition = new BlockPosition(at.getX(), at.getY(), at.getZ());
+ return e;
+ }
+
+ @Override
+ public ItemFrame getBukkitEntity() {
+ return (ItemFrame) super.getBukkitEntity();
+ }
+
+ public static class EntityItemFrameNPC extends EntityItemFrame implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityItemFrameNPC(EntityTypes extends EntityItemFrame> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityItemFrameNPC(EntityTypes extends EntityItemFrame> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new ItemFrameNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public boolean survives() {
+ return npc == null || !npc.isProtected() ? super.survives() : true;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class ItemFrameNPC extends CraftItemFrame implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public ItemFrameNPC(EntityItemFrameNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ Material id = Material.STONE;
+ int data = npc.data().get(NPC.ITEM_DATA_METADATA, npc.data().get("falling-block-data", 0));
+ if (npc.data().has(NPC.ITEM_ID_METADATA)) {
+ id = Material.getMaterial(npc.data(). get(NPC.ITEM_ID_METADATA));
+ }
+ getItem().setType(id);
+ getItem().setDurability((short) data);
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ public void setType(Material material, int data) {
+ npc.data().setPersistent(NPC.ITEM_ID_METADATA, material.name());
+ npc.data().setPersistent(NPC.ITEM_DATA_METADATA, data);
+ if (npc.isSpawned()) {
+ npc.despawn(DespawnReason.PENDING_RESPAWN);
+ npc.spawn(npc.getStoredLocation(), SpawnReason.RESPAWN);
+ }
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/LargeFireballController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/LargeFireballController.java
new file mode 100644
index 000000000..0d3ba5a18
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/LargeFireballController.java
@@ -0,0 +1,129 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftLargeFireball;
+import org.bukkit.entity.LargeFireball;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityLargeFireball;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class LargeFireballController extends MobEntityController {
+ public LargeFireballController() {
+ super(EntityLargeFireballNPC.class);
+ }
+
+ @Override
+ public LargeFireball getBukkitEntity() {
+ return (LargeFireball) super.getBukkitEntity();
+ }
+
+ public static class EntityLargeFireballNPC extends EntityLargeFireball implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityLargeFireballNPC(EntityTypes extends EntityLargeFireball> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityLargeFireballNPC(EntityTypes extends EntityLargeFireball> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new LargeFireballNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void updateSize() {
+ if (npc == null) {
+ super.updateSize();
+ } else {
+ NMSImpl.setSize(this, justCreated);
+ }
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true)) {
+ super.tick();
+ }
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class LargeFireballNPC extends CraftLargeFireball implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public LargeFireballNPC(EntityLargeFireballNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/LeashController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/LeashController.java
new file mode 100644
index 000000000..9f4b7fc31
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/LeashController.java
@@ -0,0 +1,123 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftLeash;
+import org.bukkit.entity.LeashHitch;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityLeash;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class LeashController extends MobEntityController {
+ public LeashController() {
+ super(EntityLeashNPC.class);
+ }
+
+ @Override
+ public LeashHitch getBukkitEntity() {
+ return (LeashHitch) super.getBukkitEntity();
+ }
+
+ public static class EntityLeashNPC extends EntityLeash implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityLeashNPC(EntityTypes extends EntityLeash> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityLeashNPC(EntityTypes extends EntityLeash> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new LeashNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public boolean survives() {
+ return npc == null || !npc.isProtected() ? super.survives() : true;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class LeashNPC extends CraftLeash implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public LeashNPC(EntityLeashNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/LlamaSpitController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/LlamaSpitController.java
new file mode 100644
index 000000000..e4c135641
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/LlamaSpitController.java
@@ -0,0 +1,139 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftLlamaSpit;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.LlamaSpit;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.AbstractEntityController;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityLlama;
+import net.minecraft.server.v1_15_R1.EntityLlamaSpit;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+import net.minecraft.server.v1_15_R1.WorldServer;
+
+public class LlamaSpitController extends AbstractEntityController {
+ public LlamaSpitController() {
+ super(EntityLlamaSpitNPC.class);
+ }
+
+ @Override
+ protected Entity createEntity(Location at, NPC npc) {
+ WorldServer ws = ((CraftWorld) at.getWorld()).getHandle();
+ final EntityLlamaSpitNPC handle = new EntityLlamaSpitNPC(
+ NMSImpl. getEntityType(EntityLlamaSpitNPC.class), ws, npc);
+ handle.setPositionRotation(at.getX(), at.getY(), at.getZ(), at.getPitch(), at.getYaw());
+ return handle.getBukkitEntity();
+ }
+
+ @Override
+ public LlamaSpit getBukkitEntity() {
+ return (LlamaSpit) super.getBukkitEntity();
+ }
+
+ public static class EntityLlamaSpitNPC extends EntityLlamaSpit implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityLlamaSpitNPC(EntityTypes extends EntityLlamaSpit> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityLlamaSpitNPC(EntityTypes extends EntityLlamaSpit> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ public EntityLlamaSpitNPC(World world, NPC npc, EntityLlama entity) {
+ super(world, entity);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new LlamaSpitNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true)) {
+ super.tick();
+ }
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class LlamaSpitNPC extends CraftLlamaSpit implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public LlamaSpitNPC(EntityLlamaSpitNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartChestController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartChestController.java
new file mode 100644
index 000000000..2cc70022d
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartChestController.java
@@ -0,0 +1,126 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftMinecartChest;
+import org.bukkit.entity.Minecart;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityMinecartChest;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class MinecartChestController extends MobEntityController {
+ public MinecartChestController() {
+ super(EntityMinecartChestNPC.class);
+ }
+
+ @Override
+ public Minecart getBukkitEntity() {
+ return (Minecart) super.getBukkitEntity();
+ }
+
+ public static class EntityMinecartChestNPC extends EntityMinecartChest implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityMinecartChestNPC(EntityTypes extends EntityMinecartChest> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityMinecartChestNPC(EntityTypes extends EntityMinecartChest> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public boolean damageEntity(DamageSource damagesource, float f) {
+ if (npc == null || !npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ return super.damageEntity(damagesource, f);
+ return false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new MinecartChestNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+ if (npc != null) {
+ npc.update();
+ NMSImpl.minecartItemLogic(this);
+ }
+ }
+
+ }
+
+ public static class MinecartChestNPC extends CraftMinecartChest implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public MinecartChestNPC(EntityMinecartChestNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartCommandController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartCommandController.java
new file mode 100644
index 000000000..385382cb6
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartCommandController.java
@@ -0,0 +1,125 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftMinecartCommand;
+import org.bukkit.entity.Minecart;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityMinecartCommandBlock;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class MinecartCommandController extends MobEntityController {
+ public MinecartCommandController() {
+ super(EntityMinecartCommandNPC.class);
+ }
+
+ @Override
+ public Minecart getBukkitEntity() {
+ return (Minecart) super.getBukkitEntity();
+ }
+
+ public static class EntityMinecartCommandNPC extends EntityMinecartCommandBlock implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityMinecartCommandNPC(EntityTypes extends EntityMinecartCommandBlock> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityMinecartCommandNPC(EntityTypes extends EntityMinecartCommandBlock> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public boolean damageEntity(DamageSource damagesource, float f) {
+ if (npc == null || !npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ return super.damageEntity(damagesource, f);
+ return false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new MinecartCommandNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+ if (npc != null) {
+ npc.update();
+ NMSImpl.minecartItemLogic(this);
+ }
+ }
+ }
+
+ public static class MinecartCommandNPC extends CraftMinecartCommand implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public MinecartCommandNPC(EntityMinecartCommandNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartFurnaceController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartFurnaceController.java
new file mode 100644
index 000000000..3ae8a9107
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartFurnaceController.java
@@ -0,0 +1,125 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftMinecartFurnace;
+import org.bukkit.entity.Minecart;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityMinecartFurnace;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class MinecartFurnaceController extends MobEntityController {
+ public MinecartFurnaceController() {
+ super(EntityMinecartFurnaceNPC.class);
+ }
+
+ @Override
+ public Minecart getBukkitEntity() {
+ return (Minecart) super.getBukkitEntity();
+ }
+
+ public static class EntityMinecartFurnaceNPC extends EntityMinecartFurnace implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityMinecartFurnaceNPC(EntityTypes extends EntityMinecartFurnace> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityMinecartFurnaceNPC(EntityTypes extends EntityMinecartFurnace> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public boolean damageEntity(DamageSource damagesource, float f) {
+ if (npc == null || !npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ return super.damageEntity(damagesource, f);
+ return false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new MinecartFurnaceNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+ if (npc != null) {
+ npc.update();
+ NMSImpl.minecartItemLogic(this);
+ }
+ }
+ }
+
+ public static class MinecartFurnaceNPC extends CraftMinecartFurnace implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public MinecartFurnaceNPC(EntityMinecartFurnaceNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartHopperController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartHopperController.java
new file mode 100644
index 000000000..064c78fe3
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartHopperController.java
@@ -0,0 +1,99 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import org.bukkit.entity.Minecart;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityMinecartHopper;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class MinecartHopperController extends MobEntityController {
+ public MinecartHopperController() {
+ super(EntityMinecartHopperNPC.class);
+ }
+
+ @Override
+ public Minecart getBukkitEntity() {
+ return (Minecart) super.getBukkitEntity();
+ }
+
+ public static class EntityMinecartHopperNPC extends EntityMinecartHopper implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityMinecartHopperNPC(EntityTypes extends EntityMinecartHopper> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityMinecartHopperNPC(EntityTypes extends EntityMinecartHopper> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public boolean damageEntity(DamageSource damagesource, float f) {
+ if (npc == null || !npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ return super.damageEntity(damagesource, f);
+ return false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+ if (npc != null) {
+ npc.update();
+ NMSImpl.minecartItemLogic(this);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartRideableController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartRideableController.java
new file mode 100644
index 000000000..33f938e55
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartRideableController.java
@@ -0,0 +1,125 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftMinecartRideable;
+import org.bukkit.entity.Minecart;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityMinecartRideable;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class MinecartRideableController extends MobEntityController {
+ public MinecartRideableController() {
+ super(EntityMinecartRideableNPC.class);
+ }
+
+ @Override
+ public Minecart getBukkitEntity() {
+ return (Minecart) super.getBukkitEntity();
+ }
+
+ public static class EntityMinecartRideableNPC extends EntityMinecartRideable implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityMinecartRideableNPC(EntityTypes extends EntityMinecartRideable> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityMinecartRideableNPC(EntityTypes extends EntityMinecartRideable> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public boolean damageEntity(DamageSource damagesource, float f) {
+ if (npc == null || !npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ return super.damageEntity(damagesource, f);
+ return false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new MinecartRideableNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+ if (npc != null) {
+ npc.update();
+ NMSImpl.minecartItemLogic(this);
+ }
+ }
+ }
+
+ public static class MinecartRideableNPC extends CraftMinecartRideable implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public MinecartRideableNPC(EntityMinecartRideableNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartSpawnerController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartSpawnerController.java
new file mode 100644
index 000000000..6929ec4af
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartSpawnerController.java
@@ -0,0 +1,100 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import org.bukkit.entity.Minecart;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityMinecartMobSpawner;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class MinecartSpawnerController extends MobEntityController {
+ public MinecartSpawnerController() {
+ super(EntityMinecartSpawnerNPC.class);
+ }
+
+ @Override
+ public Minecart getBukkitEntity() {
+ return (Minecart) super.getBukkitEntity();
+ }
+
+ public static class EntityMinecartSpawnerNPC extends EntityMinecartMobSpawner implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityMinecartSpawnerNPC(EntityTypes extends EntityMinecartMobSpawner> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityMinecartSpawnerNPC(EntityTypes extends EntityMinecartMobSpawner> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public boolean damageEntity(DamageSource damagesource, float f) {
+ if (npc == null || !npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ return super.damageEntity(damagesource, f);
+ return false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+ if (npc != null) {
+ npc.update();
+ NMSImpl.minecartItemLogic(this);
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartTNTController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartTNTController.java
new file mode 100644
index 000000000..1dff5a059
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/MinecartTNTController.java
@@ -0,0 +1,99 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import org.bukkit.entity.Minecart;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.EntityMinecartTNT;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class MinecartTNTController extends MobEntityController {
+ public MinecartTNTController() {
+ super(EntityMinecartTNTNPC.class);
+ }
+
+ @Override
+ public Minecart getBukkitEntity() {
+ return (Minecart) super.getBukkitEntity();
+ }
+
+ public static class EntityMinecartTNTNPC extends EntityMinecartTNT implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityMinecartTNTNPC(EntityTypes extends EntityMinecartTNT> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityMinecartTNTNPC(EntityTypes extends EntityMinecartTNT> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public boolean damageEntity(DamageSource damagesource, float f) {
+ if (npc == null || !npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ return super.damageEntity(damagesource, f);
+ return false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+ if (npc != null) {
+ npc.update();
+ NMSImpl.minecartItemLogic(this);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/PaintingController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/PaintingController.java
new file mode 100644
index 000000000..5e8749f1e
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/PaintingController.java
@@ -0,0 +1,123 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPainting;
+import org.bukkit.entity.Painting;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityPainting;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class PaintingController extends MobEntityController {
+ public PaintingController() {
+ super(EntityPaintingNPC.class);
+ }
+
+ @Override
+ public Painting getBukkitEntity() {
+ return (Painting) super.getBukkitEntity();
+ }
+
+ public static class EntityPaintingNPC extends EntityPainting implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityPaintingNPC(EntityTypes extends EntityPainting> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityPaintingNPC(EntityTypes extends EntityPainting> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new PaintingNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public boolean survives() {
+ return npc == null || !npc.isProtected() ? super.survives() : true;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class PaintingNPC extends CraftPainting implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public PaintingNPC(EntityPaintingNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ShulkerBulletController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ShulkerBulletController.java
new file mode 100644
index 000000000..d5eb0d230
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ShulkerBulletController.java
@@ -0,0 +1,118 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftShulkerBullet;
+import org.bukkit.entity.ShulkerBullet;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityShulkerBullet;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class ShulkerBulletController extends MobEntityController {
+ public ShulkerBulletController() {
+ super(EntityShulkerBulletNPC.class);
+ }
+
+ @Override
+ public ShulkerBullet getBukkitEntity() {
+ return (ShulkerBullet) super.getBukkitEntity();
+ }
+
+ public static class EntityShulkerBulletNPC extends EntityShulkerBullet implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityShulkerBulletNPC(EntityTypes extends EntityShulkerBullet> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityShulkerBulletNPC(EntityTypes extends EntityShulkerBullet> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new ShulkerBulletNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class ShulkerBulletNPC extends CraftShulkerBullet implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public ShulkerBulletNPC(EntityShulkerBulletNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/SmallFireballController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/SmallFireballController.java
new file mode 100644
index 000000000..b66049e0f
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/SmallFireballController.java
@@ -0,0 +1,121 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftSmallFireball;
+import org.bukkit.entity.SmallFireball;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntitySmallFireball;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class SmallFireballController extends MobEntityController {
+ public SmallFireballController() {
+ super(EntitySmallFireballNPC.class);
+ }
+
+ @Override
+ public SmallFireball getBukkitEntity() {
+ return (SmallFireball) super.getBukkitEntity();
+ }
+
+ public static class EntitySmallFireballNPC extends EntitySmallFireball implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntitySmallFireballNPC(EntityTypes extends EntitySmallFireball> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntitySmallFireballNPC(EntityTypes extends EntitySmallFireball> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new SmallFireballNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true)) {
+ super.tick();
+ }
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class SmallFireballNPC extends CraftSmallFireball implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public SmallFireballNPC(EntitySmallFireballNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/SnowballController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/SnowballController.java
new file mode 100644
index 000000000..3d49001d2
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/SnowballController.java
@@ -0,0 +1,118 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftSnowball;
+import org.bukkit.entity.Snowball;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntitySnowball;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class SnowballController extends MobEntityController {
+ public SnowballController() {
+ super(EntitySnowballNPC.class);
+ }
+
+ @Override
+ public Snowball getBukkitEntity() {
+ return (Snowball) super.getBukkitEntity();
+ }
+
+ public static class EntitySnowballNPC extends EntitySnowball implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntitySnowballNPC(EntityTypes extends EntitySnowball> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntitySnowballNPC(EntityTypes extends EntitySnowball> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new SnowballNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class SnowballNPC extends CraftSnowball implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public SnowballNPC(EntitySnowballNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/SpectralArrowController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/SpectralArrowController.java
new file mode 100644
index 000000000..9857d5e55
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/SpectralArrowController.java
@@ -0,0 +1,118 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftArrow;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.Arrow;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntitySpectralArrow;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class SpectralArrowController extends MobEntityController {
+ public SpectralArrowController() {
+ super(EntitySpectralArrowNPC.class);
+ }
+
+ @Override
+ public Arrow getBukkitEntity() {
+ return (Arrow) super.getBukkitEntity();
+ }
+
+ public static class EntitySpectralArrowNPC extends EntitySpectralArrow implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntitySpectralArrowNPC(EntityTypes extends EntitySpectralArrow> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntitySpectralArrowNPC(EntityTypes extends EntitySpectralArrow> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new SpectralArrowNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class SpectralArrowNPC extends CraftArrow implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public SpectralArrowNPC(EntitySpectralArrowNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/TNTPrimedController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/TNTPrimedController.java
new file mode 100644
index 000000000..56b5a2c31
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/TNTPrimedController.java
@@ -0,0 +1,118 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftTNTPrimed;
+import org.bukkit.entity.TNTPrimed;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityTNTPrimed;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class TNTPrimedController extends MobEntityController {
+ public TNTPrimedController() {
+ super(EntityTNTPrimedNPC.class);
+ }
+
+ @Override
+ public TNTPrimed getBukkitEntity() {
+ return (TNTPrimed) super.getBukkitEntity();
+ }
+
+ public static class EntityTNTPrimedNPC extends EntityTNTPrimed implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityTNTPrimedNPC(EntityTypes extends EntityTNTPrimed> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityTNTPrimedNPC(EntityTypes extends EntityTNTPrimed> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new TNTPrimedNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class TNTPrimedNPC extends CraftTNTPrimed implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public TNTPrimedNPC(EntityTNTPrimedNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ThrownExpBottleController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ThrownExpBottleController.java
new file mode 100644
index 000000000..9edff4a18
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ThrownExpBottleController.java
@@ -0,0 +1,121 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftThrownExpBottle;
+import org.bukkit.entity.ThrownExpBottle;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityThrownExpBottle;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class ThrownExpBottleController extends MobEntityController {
+ public ThrownExpBottleController() {
+ super(EntityThrownExpBottleNPC.class);
+ }
+
+ @Override
+ public ThrownExpBottle getBukkitEntity() {
+ return (ThrownExpBottle) super.getBukkitEntity();
+ }
+
+ public static class EntityThrownExpBottleNPC extends EntityThrownExpBottle implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityThrownExpBottleNPC(EntityTypes extends EntityThrownExpBottle> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityThrownExpBottleNPC(EntityTypes extends EntityThrownExpBottle> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new ThrownExpBottleNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true)) {
+ super.tick();
+ }
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class ThrownExpBottleNPC extends CraftThrownExpBottle implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public ThrownExpBottleNPC(EntityThrownExpBottleNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ThrownPotionController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ThrownPotionController.java
new file mode 100644
index 000000000..81f57488d
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ThrownPotionController.java
@@ -0,0 +1,137 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftThrownPotion;
+import org.bukkit.entity.ThrownPotion;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityPotion;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.Items;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class ThrownPotionController extends MobEntityController {
+ public ThrownPotionController() {
+ super(EntityThrownPotionNPC.class);
+ }
+
+ @Override
+ public ThrownPotion getBukkitEntity() {
+ return (ThrownPotion) super.getBukkitEntity();
+ }
+
+ public static class EntityThrownPotionNPC extends EntityPotion implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityThrownPotionNPC(EntityTypes extends EntityPotion> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityThrownPotionNPC(EntityTypes extends EntityPotion> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ if (getItem() != null && getItem().getItem().equals(Items.LINGERING_POTION)) {
+ NMSImpl.setBukkitEntity(this, new LingeringThrownPotionNPC(this));
+ } else {
+ NMSImpl.setBukkitEntity(this, new SplashThrownPotionNPC(this));
+ }
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class LingeringThrownPotionNPC extends CraftThrownPotion implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public LingeringThrownPotionNPC(EntityThrownPotionNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+
+ public static class SplashThrownPotionNPC extends CraftThrownPotion implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public SplashThrownPotionNPC(EntityThrownPotionNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ThrownTridentController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ThrownTridentController.java
new file mode 100644
index 000000000..5b2d050dc
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/ThrownTridentController.java
@@ -0,0 +1,118 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftTrident;
+import org.bukkit.entity.Trident;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityThrownTrident;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class ThrownTridentController extends MobEntityController {
+ public ThrownTridentController() {
+ super(EntityThrownTridentNPC.class);
+ }
+
+ @Override
+ public Trident getBukkitEntity() {
+ return (Trident) super.getBukkitEntity();
+ }
+
+ public static class EntityThrownTridentNPC extends EntityThrownTrident implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityThrownTridentNPC(EntityTypes extends EntityThrownTrident> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityThrownTridentNPC(EntityTypes extends EntityThrownTrident> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new ThrownTridentNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class ThrownTridentNPC extends CraftTrident implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public ThrownTridentNPC(EntityThrownTridentNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/TippedArrowController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/TippedArrowController.java
new file mode 100644
index 000000000..9a66bd74c
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/TippedArrowController.java
@@ -0,0 +1,118 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftArrow;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.Arrow;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityTippedArrow;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class TippedArrowController extends MobEntityController {
+ public TippedArrowController() {
+ super(EntityTippedArrowNPC.class);
+ }
+
+ @Override
+ public Arrow getBukkitEntity() {
+ return (Arrow) super.getBukkitEntity();
+ }
+
+ public static class EntityTippedArrowNPC extends EntityTippedArrow implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityTippedArrowNPC(EntityTypes extends EntityTippedArrow> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityTippedArrowNPC(EntityTypes extends EntityTippedArrow> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new TippedArrowNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class TippedArrowNPC extends CraftArrow implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public TippedArrowNPC(EntityTippedArrowNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/WitherSkullController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/WitherSkullController.java
new file mode 100644
index 000000000..1e9ae7947
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/nonliving/WitherSkullController.java
@@ -0,0 +1,118 @@
+package net.citizensnpcs.nms.v1_15_R1.entity.nonliving;
+
+import net.citizensnpcs.nms.v1_15_R1.entity.MobEntityController;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftWitherSkull;
+import org.bukkit.entity.WitherSkull;
+import org.bukkit.util.Vector;
+
+import net.citizensnpcs.api.event.NPCPushEvent;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.CitizensNPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EntityWitherSkull;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.World;
+
+public class WitherSkullController extends MobEntityController {
+ public WitherSkullController() {
+ super(EntityWitherSkullNPC.class);
+ }
+
+ @Override
+ public WitherSkull getBukkitEntity() {
+ return (WitherSkull) super.getBukkitEntity();
+ }
+
+ public static class EntityWitherSkullNPC extends EntityWitherSkull implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public EntityWitherSkullNPC(EntityTypes extends EntityWitherSkull> types, World world) {
+ this(types, world, null);
+ }
+
+ public EntityWitherSkullNPC(EntityTypes extends EntityWitherSkull> types, World world, NPC npc) {
+ super(types, world);
+ this.npc = (CitizensNPC) npc;
+ }
+
+ @Override
+ public void collide(net.minecraft.server.v1_15_R1.Entity entity) {
+ // this method is called by both the entities involved - cancelling
+ // it will not stop the NPC from moving.
+ super.collide(entity);
+ if (npc != null) {
+ Util.callCollisionEvent(npc, entity.getBukkitEntity());
+ }
+ }
+
+ @Override
+ public boolean d(NBTTagCompound save) {
+ return npc == null ? super.d(save) : false;
+ }
+
+ @Override
+ public void f(double x, double y, double z) {
+ if (npc == null) {
+ super.f(x, y, z);
+ return;
+ }
+ if (NPCPushEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ if (!npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true))
+ super.f(x, y, z);
+ return;
+ }
+ Vector vector = new Vector(x, y, z);
+ NPCPushEvent event = Util.callPushEvent(npc, vector);
+ if (!event.isCancelled()) {
+ vector = event.getCollisionVector();
+ super.f(vector.getX(), vector.getY(), vector.getZ());
+ }
+ // when another entity collides, this method is called to push the
+ // NPC so we prevent it from doing anything if the event is
+ // cancelled.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
+ NMSImpl.setBukkitEntity(this, new WitherSkullNPC(this));
+ }
+ return super.getBukkitEntity();
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+
+ @Override
+ public void tick() {
+ if (npc != null) {
+ npc.update();
+ } else {
+ super.tick();
+ }
+ }
+ }
+
+ public static class WitherSkullNPC extends CraftWitherSkull implements NPCHolder {
+ private final CitizensNPC npc;
+
+ public WitherSkullNPC(EntityWitherSkullNPC entity) {
+ super((CraftServer) Bukkit.getServer(), entity);
+ this.npc = entity.npc;
+ }
+
+ @Override
+ public NPC getNPC() {
+ return npc;
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/network/EmptyChannel.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/network/EmptyChannel.java
new file mode 100644
index 000000000..7a0d11174
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/network/EmptyChannel.java
@@ -0,0 +1,80 @@
+package net.citizensnpcs.nms.v1_15_R1.network;
+
+import java.net.SocketAddress;
+
+import io.netty.channel.AbstractChannel;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelConfig;
+import io.netty.channel.ChannelMetadata;
+import io.netty.channel.ChannelOutboundBuffer;
+import io.netty.channel.DefaultChannelConfig;
+import io.netty.channel.EventLoop;
+
+public class EmptyChannel extends AbstractChannel {
+ private final ChannelConfig config = new DefaultChannelConfig(this);
+
+ public EmptyChannel(Channel parent) {
+ super(parent);
+ }
+
+ @Override
+ public ChannelConfig config() {
+ config.setAutoRead(true);
+ return config;
+ }
+
+ @Override
+ protected void doBeginRead() throws Exception {
+ }
+
+ @Override
+ protected void doBind(SocketAddress arg0) throws Exception {
+ }
+
+ @Override
+ protected void doClose() throws Exception {
+ }
+
+ @Override
+ protected void doDisconnect() throws Exception {
+ }
+
+ @Override
+ protected void doWrite(ChannelOutboundBuffer arg0) throws Exception {
+ }
+
+ @Override
+ public boolean isActive() {
+ return false;
+ }
+
+ @Override
+ protected boolean isCompatible(EventLoop arg0) {
+ return true;
+ }
+
+ @Override
+ public boolean isOpen() {
+ return false;
+ }
+
+ @Override
+ protected SocketAddress localAddress0() {
+ return null;
+ }
+
+ @Override
+ public ChannelMetadata metadata() {
+ return new ChannelMetadata(true);
+ }
+
+ @Override
+ protected AbstractUnsafe newUnsafe() {
+ return null;
+ }
+
+ @Override
+ protected SocketAddress remoteAddress0() {
+ return null;
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/network/EmptyNetHandler.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/network/EmptyNetHandler.java
new file mode 100644
index 000000000..ab16b1b3e
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/network/EmptyNetHandler.java
@@ -0,0 +1,17 @@
+package net.citizensnpcs.nms.v1_15_R1.network;
+
+import net.minecraft.server.v1_15_R1.EntityPlayer;
+import net.minecraft.server.v1_15_R1.MinecraftServer;
+import net.minecraft.server.v1_15_R1.NetworkManager;
+import net.minecraft.server.v1_15_R1.Packet;
+import net.minecraft.server.v1_15_R1.PlayerConnection;
+
+public class EmptyNetHandler extends PlayerConnection {
+ public EmptyNetHandler(MinecraftServer minecraftServer, NetworkManager networkManager, EntityPlayer entityPlayer) {
+ super(minecraftServer, networkManager, entityPlayer);
+ }
+
+ @Override
+ public void sendPacket(Packet> packet) {
+ }
+}
\ No newline at end of file
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/network/EmptyNetworkManager.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/network/EmptyNetworkManager.java
new file mode 100644
index 000000000..4f6862244
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/network/EmptyNetworkManager.java
@@ -0,0 +1,25 @@
+package net.citizensnpcs.nms.v1_15_R1.network;
+
+import java.io.IOException;
+
+import io.netty.util.concurrent.GenericFutureListener;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.minecraft.server.v1_15_R1.EnumProtocolDirection;
+import net.minecraft.server.v1_15_R1.NetworkManager;
+import net.minecraft.server.v1_15_R1.Packet;
+
+public class EmptyNetworkManager extends NetworkManager {
+ public EmptyNetworkManager(EnumProtocolDirection flag) throws IOException {
+ super(flag);
+ NMSImpl.initNetworkManager(this);
+ }
+
+ @Override
+ public boolean isConnected() {
+ return true;
+ }
+
+ @Override
+ public void sendPacket(Packet packet, GenericFutureListener genericfuturelistener) {
+ }
+}
\ No newline at end of file
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/network/EmptySocket.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/network/EmptySocket.java
new file mode 100644
index 000000000..fa3bedc5e
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/network/EmptySocket.java
@@ -0,0 +1,21 @@
+package net.citizensnpcs.nms.v1_15_R1.network;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+public class EmptySocket extends Socket {
+ @Override
+ public InputStream getInputStream() {
+ return new ByteArrayInputStream(EMPTY);
+ }
+
+ @Override
+ public OutputStream getOutputStream() {
+ return new ByteArrayOutputStream(10);
+ }
+
+ private static final byte[] EMPTY = new byte[50];
+}
\ No newline at end of file
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/BeeTrait.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/BeeTrait.java
new file mode 100644
index 000000000..50a0be088
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/BeeTrait.java
@@ -0,0 +1,51 @@
+package net.citizensnpcs.nms.v1_15_R1.trait;
+
+import org.bukkit.entity.Bee;
+
+import net.citizensnpcs.api.persistence.Persist;
+import net.citizensnpcs.api.trait.Trait;
+import net.citizensnpcs.api.trait.TraitName;
+
+@TraitName("beetrait")
+public class BeeTrait extends Trait {
+ @Persist
+ private int anger;
+ @Persist
+ private boolean nectar = false;
+ @Persist
+ private boolean stung = false;
+
+ public BeeTrait() {
+ super("beetrait");
+ }
+
+ public boolean hasNectar() {
+ return nectar;
+ }
+
+ public boolean hasStung() {
+ return stung;
+ }
+
+ @Override
+ public void run() {
+ if (npc.isSpawned() && npc.getEntity() instanceof Bee) {
+ Bee bee = (Bee) npc.getEntity();
+ bee.setHasStung(stung);
+ bee.setAnger(anger);
+ bee.setHasNectar(nectar);
+ }
+ }
+
+ public void setAnger(int anger) {
+ this.anger = anger;
+ }
+
+ public void setNectar(boolean nectar) {
+ this.nectar = nectar;
+ }
+
+ public void setStung(boolean stung) {
+ this.stung = stung;
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/BossBarTrait.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/BossBarTrait.java
new file mode 100644
index 000000000..750bd95c4
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/BossBarTrait.java
@@ -0,0 +1,77 @@
+package net.citizensnpcs.nms.v1_15_R1.trait;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.bukkit.boss.BarColor;
+import org.bukkit.boss.BarFlag;
+import org.bukkit.boss.BossBar;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.EntityType;
+
+import com.google.common.collect.Lists;
+
+import net.citizensnpcs.api.persistence.Persist;
+import net.citizensnpcs.api.trait.Trait;
+import net.citizensnpcs.api.trait.TraitName;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+@TraitName("bossbar")
+public class BossBarTrait extends Trait {
+ @Persist("color")
+ private BarColor color = null;
+ @Persist("flags")
+ private List flags = Lists.newArrayList();
+ @Persist("title")
+ private String title = null;
+ @Persist("visible")
+ private boolean visible = true;
+
+ public BossBarTrait() {
+ super("bossbar");
+ }
+
+ private boolean isBoss(Entity entity) {
+ return entity.getType() == EntityType.ENDER_DRAGON || entity.getType() == EntityType.WITHER
+ || entity.getType() == EntityType.GUARDIAN;
+ }
+
+ @Override
+ public void run() {
+ if (!npc.isSpawned() || !isBoss(npc.getEntity()))
+ return;
+ BossBar bar = NMSImpl.getBossBar(npc.getEntity());
+ if (bar == null) {
+ return;
+ }
+ bar.setVisible(visible);
+ if (color != null) {
+ bar.setColor(color);
+ }
+ if (title != null) {
+ bar.setTitle(title);
+ }
+ for (BarFlag flag : BarFlag.values()) {
+ bar.removeFlag(flag);
+ }
+ for (BarFlag flag : flags) {
+ bar.addFlag(flag);
+ }
+ }
+
+ public void setColor(BarColor color) {
+ this.color = color;
+ }
+
+ public void setFlags(Collection flags) {
+ this.flags = Lists.newArrayList(flags);
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public void setVisible(boolean visible) {
+ this.visible = visible;
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/CatTrait.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/CatTrait.java
new file mode 100644
index 000000000..3527373ec
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/CatTrait.java
@@ -0,0 +1,58 @@
+package net.citizensnpcs.nms.v1_15_R1.trait;
+
+import org.bukkit.DyeColor;
+import org.bukkit.entity.Cat;
+
+import net.citizensnpcs.api.persistence.Persist;
+import net.citizensnpcs.api.trait.Trait;
+import net.citizensnpcs.api.trait.TraitName;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+
+@TraitName("cattrait")
+public class CatTrait extends Trait {
+ @Persist
+ private DyeColor collarColor = null;
+ @Persist
+ private boolean lying = false;
+ @Persist
+ private boolean sitting = false;
+ @Persist
+ private Cat.Type type = Cat.Type.BLACK;
+
+ public CatTrait() {
+ super("cattrait");
+ }
+
+ public boolean isLyingDown() {
+ return lying;
+ }
+
+ @Override
+ public void run() {
+ if (npc.isSpawned() && npc.getEntity() instanceof Cat) {
+ Cat cat = (Cat) npc.getEntity();
+ cat.setSitting(sitting);
+ cat.setCatType(type);
+ if (collarColor != null) {
+ cat.setCollarColor(collarColor);
+ }
+ NMSImpl.setLyingDown(cat, lying);
+ }
+ }
+
+ public void setCollarColor(DyeColor color) {
+ this.collarColor = color;
+ }
+
+ public void setLyingDown(boolean lying) {
+ this.lying = lying;
+ }
+
+ public void setSitting(boolean sitting) {
+ this.sitting = sitting;
+ }
+
+ public void setType(Cat.Type type) {
+ this.type = type;
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/Commands.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/Commands.java
new file mode 100644
index 000000000..24d0c5f97
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/Commands.java
@@ -0,0 +1,492 @@
+package net.citizensnpcs.nms.v1_15_R1.trait;
+
+import java.util.List;
+
+import org.bukkit.DyeColor;
+import org.bukkit.boss.BarColor;
+import org.bukkit.boss.BarFlag;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Cat;
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.Fox;
+import org.bukkit.entity.Llama.Color;
+import org.bukkit.entity.MushroomCow;
+import org.bukkit.entity.Panda;
+import org.bukkit.entity.Parrot.Variant;
+import org.bukkit.entity.TropicalFish.Pattern;
+import org.bukkit.entity.Villager;
+import org.bukkit.entity.Villager.Profession;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+
+import net.citizensnpcs.api.command.Command;
+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.CommandUsageException;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.api.util.Colorizer;
+import net.citizensnpcs.api.util.Messaging;
+import net.citizensnpcs.trait.VillagerProfession;
+import net.citizensnpcs.util.Messages;
+import net.citizensnpcs.util.Util;
+
+public class Commands {
+ @Command(
+ aliases = { "npc" },
+ usage = "bee (-s/-n) --anger anger",
+ desc = "Sets bee modifiers",
+ modifiers = { "bee" },
+ min = 1,
+ max = 1,
+ flags = "sn",
+ permission = "citizens.npc.bee")
+ @Requirements(selected = true, ownership = true, types = EntityType.BEE)
+ public void bee(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
+ BeeTrait trait = npc.getTrait(BeeTrait.class);
+ String output = "";
+ if (args.hasValueFlag("anger")) {
+ int anger = args.getFlagInteger("anger");
+ if (anger < 0) {
+ throw new CommandException(Messages.INVALID_BEE_ANGER);
+ }
+ trait.setAnger(anger);
+ output += ' ' + Messaging.tr(Messages.BEE_ANGER_SET, args.getFlag("anger"));
+ }
+ if (args.hasFlag('s')) {
+ trait.setStung(!trait.hasStung());
+ output += ' ' + (trait.hasStung() ? Messaging.tr(Messages.BEE_STUNG, npc.getName())
+ : Messaging.tr(Messages.BEE_NOT_STUNG, npc.getName()));
+ }
+ if (args.hasFlag('n')) {
+ trait.setNectar(!trait.hasNectar());
+ output += ' ' + (trait.hasStung() ? Messaging.tr(Messages.BEE_HAS_NECTAR, npc.getName())
+ : Messaging.tr(Messages.BEE_NO_NECTAR, npc.getName()));
+ }
+ if (!output.isEmpty()) {
+ Messaging.send(sender, output.trim());
+ } else {
+ throw new CommandUsageException();
+ }
+ }
+
+ @Command(
+ aliases = { "npc" },
+ usage = "bossbar --color [color] --title [title] --visible [visible] --flags [flags]",
+ desc = "Edit bossbar properties",
+ modifiers = { "bossbar" },
+ min = 1,
+ max = 1)
+ @Requirements(selected = true, ownership = true, types = { EntityType.WITHER, EntityType.ENDER_DRAGON })
+ public void bossbar(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
+ BossBarTrait trait = npc.getTrait(BossBarTrait.class);
+ if (args.hasValueFlag("color")) {
+ BarColor color = Util.matchEnum(BarColor.values(), args.getFlag("color"));
+ trait.setColor(color);
+ }
+
+ if (args.hasValueFlag("title")) {
+ trait.setTitle(Colorizer.parseColors(args.getFlag("title")));
+ }
+ if (args.hasValueFlag("visible")) {
+ trait.setVisible(Boolean.parseBoolean(args.getFlag("visible")));
+ }
+ if (args.hasValueFlag("flags")) {
+ List flags = Lists.newArrayList();
+ for (String s : Splitter.on(',').omitEmptyStrings().trimResults().split(args.getFlag("flags"))) {
+ BarFlag flag = Util.matchEnum(BarFlag.values(), s);
+ if (flag != null) {
+ flags.add(flag);
+ }
+ }
+ trait.setFlags(flags);
+ }
+ }
+
+ @Command(
+ aliases = { "npc" },
+ usage = "cat (-s/-n/-l) --type type --ccolor collar color",
+ desc = "Sets cat modifiers",
+ modifiers = { "cat" },
+ min = 1,
+ max = 1,
+ flags = "snl",
+ permission = "citizens.npc.cat")
+ @Requirements(selected = true, ownership = true, types = EntityType.CAT)
+ public void cat(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
+ CatTrait trait = npc.getTrait(CatTrait.class);
+ String output = "";
+ if (args.hasValueFlag("type")) {
+ Cat.Type type = Util.matchEnum(Cat.Type.values(), args.getFlag("type"));
+ if (type == null) {
+ throw new CommandUsageException(Messages.INVALID_CAT_TYPE, Util.listValuesPretty(Cat.Type.values()));
+ }
+ trait.setType(type);
+ output += ' ' + Messaging.tr(Messages.CAT_TYPE_SET, args.getFlag("type"));
+ }
+ if (args.hasValueFlag("ccolor")) {
+ DyeColor color = Util.matchEnum(DyeColor.values(), args.getFlag("ccolor"));
+ if (color == null) {
+ throw new CommandUsageException(Messages.INVALID_CAT_COLLAR_COLOR,
+ Util.listValuesPretty(DyeColor.values()));
+ }
+ trait.setCollarColor(color);
+ output += ' ' + Messaging.tr(Messages.CAT_COLLAR_COLOR_SET, args.getFlag("ccolor"));
+ }
+ if (args.hasFlag('s')) {
+ trait.setSitting(true);
+ output += ' ' + Messaging.tr(Messages.CAT_STARTED_SITTING, npc.getName());
+ } else if (args.hasFlag('n')) {
+ trait.setSitting(false);
+ output += ' ' + Messaging.tr(Messages.CAT_STOPPED_SITTING, npc.getName());
+ }
+ if (args.hasFlag('l')) {
+ trait.setLyingDown(!trait.isLyingDown());
+ output += ' ' + Messaging.tr(trait.isLyingDown() ? Messages.CAT_STARTED_LYING : Messages.CAT_STOPPED_LYING,
+ npc.getName());
+ }
+ if (!output.isEmpty()) {
+ Messaging.send(sender, output.trim());
+ } else {
+ throw new CommandUsageException();
+ }
+ }
+
+ @Command(
+ aliases = { "npc" },
+ usage = "fox --type type --sleeping [true|false] --sitting [true|false] --crouching [true|false]",
+ desc = "Sets fox modifiers",
+ modifiers = { "fox" },
+ min = 1,
+ max = 1,
+ permission = "citizens.npc.fox")
+ @Requirements(selected = true, ownership = true, types = EntityType.FOX)
+ public void fox(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
+ FoxTrait trait = npc.getTrait(FoxTrait.class);
+ String output = "";
+ if (args.hasValueFlag("type")) {
+ Fox.Type type = Util.matchEnum(Fox.Type.values(), args.getFlag("type"));
+ if (type == null) {
+ throw new CommandUsageException(Messages.INVALID_FOX_TYPE, Util.listValuesPretty(Fox.Type.values()));
+ }
+ trait.setType(type);
+ output += ' ' + Messaging.tr(Messages.FOX_TYPE_SET, args.getFlag("type"));
+ }
+ if (args.hasValueFlag("sleeping")) {
+ boolean sleeping = Boolean.parseBoolean(args.getFlag("sleeping"));
+ trait.setSleeping(sleeping);
+ output += ' ' + Messaging.tr(sleeping ? Messages.FOX_SLEEPING_SET : Messages.FOX_SLEEPING_UNSET);
+ }
+ if (args.hasValueFlag("sitting")) {
+ boolean sitting = Boolean.parseBoolean(args.getFlag("sitting"));
+ trait.setSitting(sitting);
+ output += ' ' + Messaging.tr(sitting ? Messages.FOX_SITTING_SET : Messages.FOX_SITTING_UNSET);
+ }
+ if (args.hasValueFlag("crouching")) {
+ boolean crouching = Boolean.parseBoolean(args.getFlag("crouching"));
+ trait.setCrouching(crouching);
+ output += ' ' + Messaging.tr(crouching ? Messages.FOX_CROUCHING_SET : Messages.FOX_CROUCHING_UNSET);
+ }
+ if (!output.isEmpty()) {
+ Messaging.send(sender, output.trim());
+ } else {
+ throw new CommandUsageException();
+ }
+ }
+
+ @Command(
+ aliases = { "npc" },
+ usage = "llama (--color color) (--strength strength)",
+ desc = "Sets llama modifiers",
+ modifiers = { "llama" },
+ min = 1,
+ max = 1,
+ permission = "citizens.npc.llama")
+ @Requirements(selected = true, ownership = true, types = { EntityType.LLAMA, EntityType.TRADER_LLAMA })
+ public void llama(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
+ LlamaTrait trait = npc.getTrait(LlamaTrait.class);
+ String output = "";
+ if (args.hasValueFlag("color") || args.hasValueFlag("colour")) {
+ String colorRaw = args.getFlag("color", args.getFlag("colour"));
+ Color color = Util.matchEnum(Color.values(), colorRaw);
+ if (color == null) {
+ String valid = Util.listValuesPretty(Color.values());
+ throw new CommandException(Messages.INVALID_LLAMA_COLOR, valid);
+ }
+ trait.setColor(color);
+ output += Messaging.tr(Messages.LLAMA_COLOR_SET, Util.prettyEnum(color));
+ }
+ if (args.hasValueFlag("strength")) {
+ trait.setStrength(Math.max(1, Math.min(5, args.getFlagInteger("strength"))));
+ output += Messaging.tr(Messages.LLAMA_STRENGTH_SET, args.getFlagInteger("strength"));
+ }
+ if (!output.isEmpty()) {
+ Messaging.send(sender, output);
+ }
+ }
+
+ @Command(
+ aliases = { "npc" },
+ usage = "mcow (--variant [variant])",
+ desc = "Sets mushroom cow modifiers.",
+ modifiers = { "mcow", "mushroomcow" },
+ min = 1,
+ max = 1,
+ permission = "citizens.npc.mushroomcow")
+ @Requirements(selected = true, ownership = true, types = { EntityType.SHULKER })
+ public void mushroomcow(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
+ MushroomCowTrait trait = npc.getTrait(MushroomCowTrait.class);
+ boolean hasArg = false;
+ if (args.hasValueFlag("variant")) {
+ MushroomCow.Variant variant = Util.matchEnum(MushroomCow.Variant.values(), args.getFlag("variant"));
+ if (variant == null) {
+ Messaging.sendErrorTr(sender, Messages.INVALID_MUSHROOM_COW_VARIANT,
+ Util.listValuesPretty(MushroomCow.Variant.values()));
+ return;
+ }
+ trait.setVariant(variant);
+ Messaging.sendTr(sender, Messages.MUSHROOM_COW_VARIANT_SET, npc.getName(), variant);
+ hasArg = true;
+ }
+ if (!hasArg) {
+ throw new CommandUsageException();
+ }
+ }
+
+ @Command(
+ aliases = { "npc" },
+ usage = "panda --gene (main gene) --hgene (hidden gene)",
+ desc = "Sets panda modifiers",
+ modifiers = { "panda" },
+ min = 1,
+ max = 1,
+ permission = "citizens.npc.panda")
+ @Requirements(selected = true, ownership = true, types = EntityType.PANDA)
+ public void panda(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
+ PandaTrait trait = npc.getTrait(PandaTrait.class);
+ String output = "";
+ if (args.hasValueFlag("gene")) {
+ Panda.Gene gene = Util.matchEnum(Panda.Gene.values(), args.getFlag("gene"));
+ if (gene == null) {
+ throw new CommandUsageException(Messages.INVALID_PANDA_GENE,
+ Util.listValuesPretty(Panda.Gene.values()));
+ }
+ trait.setMainGene(gene);
+ output += ' ' + Messaging.tr(Messages.PANDA_MAIN_GENE_SET, args.getFlag("gene"));
+ }
+ if (args.hasValueFlag("hgene")) {
+ Panda.Gene gene = Util.matchEnum(Panda.Gene.values(), args.getFlag("hgene"));
+ if (gene == null) {
+ throw new CommandUsageException(Messages.INVALID_PANDA_GENE,
+ Util.listValuesPretty(Panda.Gene.values()));
+ }
+ trait.setHiddenGene(gene);
+ output += ' ' + Messaging.tr(Messages.PANDA_HIDDEN_GENE_SET, args.getFlag("hgene"));
+ }
+ if (!output.isEmpty()) {
+ Messaging.send(sender, output.trim());
+ } else {
+ throw new CommandUsageException();
+ }
+ }
+
+ @Command(
+ aliases = { "npc" },
+ usage = "parrot (--variant variant)",
+ desc = "Sets parrot modifiers",
+ modifiers = { "parrot" },
+ min = 1,
+ max = 1,
+ permission = "citizens.npc.parrot")
+ @Requirements(selected = true, ownership = true, types = EntityType.PARROT)
+ public void parrot(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
+ ParrotTrait trait = npc.getTrait(ParrotTrait.class);
+ String output = "";
+ if (args.hasValueFlag("variant")) {
+ String variantRaw = args.getFlag("variant");
+ Variant variant = Util.matchEnum(Variant.values(), variantRaw);
+ if (variant == null) {
+ String valid = Util.listValuesPretty(Variant.values());
+ throw new CommandException(Messages.INVALID_PARROT_VARIANT, valid);
+ }
+ trait.setVariant(variant);
+ output += Messaging.tr(Messages.PARROT_VARIANT_SET, Util.prettyEnum(variant));
+ }
+ if (!output.isEmpty()) {
+ Messaging.send(sender, output);
+ }
+ }
+
+ @Command(
+ aliases = { "npc" },
+ usage = "phantom (--size size)",
+ desc = "Sets phantom modifiers",
+ modifiers = { "phantom" },
+ min = 1,
+ max = 1,
+ permission = "citizens.npc.phantom")
+ @Requirements(selected = true, ownership = true, types = EntityType.PHANTOM)
+ public void phantom(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
+ PhantomTrait trait = npc.getTrait(PhantomTrait.class);
+ String output = "";
+ if (args.hasValueFlag("size")) {
+ if (args.getFlagInteger("size") <= 0) {
+ throw new CommandUsageException();
+ }
+ trait.setSize(args.getFlagInteger("size"));
+ output += Messaging.tr(Messages.PHANTOM_STATE_SET, args.getFlagInteger("size"));
+ }
+ if (!output.isEmpty()) {
+ Messaging.send(sender, output);
+ } else {
+ throw new CommandUsageException();
+ }
+ }
+
+ @Command(
+ aliases = { "npc" },
+ usage = "pufferfish (--state state)",
+ desc = "Sets pufferfish modifiers",
+ modifiers = { "pufferfish" },
+ min = 1,
+ max = 1,
+ permission = "citizens.npc.pufferfish")
+ @Requirements(selected = true, ownership = true, types = EntityType.PUFFERFISH)
+ public void pufferfish(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
+ PufferFishTrait trait = npc.getTrait(PufferFishTrait.class);
+ String output = "";
+ if (args.hasValueFlag("state")) {
+ int state = Math.min(Math.max(args.getFlagInteger("state"), 0), 3);
+ trait.setPuffState(state);
+ output += Messaging.tr(Messages.PUFFERFISH_STATE_SET, state);
+ }
+ if (!output.isEmpty()) {
+ Messaging.send(sender, output);
+ }
+ }
+
+ @Command(
+ aliases = { "npc" },
+ usage = "shulker (--peek [peek] --color [color])",
+ desc = "Sets shulker modifiers.",
+ modifiers = { "shulker" },
+ min = 1,
+ max = 1,
+ permission = "citizens.npc.shulker")
+ @Requirements(selected = true, ownership = true, types = { EntityType.SHULKER })
+ public void shulker(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
+ ShulkerTrait trait = npc.getTrait(ShulkerTrait.class);
+ boolean hasArg = false;
+ if (args.hasValueFlag("peek")) {
+ int peek = (byte) args.getFlagInteger("peek");
+ trait.setPeek(peek);
+ Messaging.sendTr(sender, Messages.SHULKER_PEEK_SET, npc.getName(), peek);
+ hasArg = true;
+ }
+ if (args.hasValueFlag("color")) {
+ DyeColor color = Util.matchEnum(DyeColor.values(), args.getFlag("color"));
+ if (color == null) {
+ Messaging.sendErrorTr(sender, Messages.INVALID_SHULKER_COLOR, Util.listValuesPretty(DyeColor.values()));
+ return;
+ }
+ trait.setColor(color);
+ Messaging.sendTr(sender, Messages.SHULKER_COLOR_SET, npc.getName(), Util.prettyEnum(color));
+ hasArg = true;
+ }
+ if (!hasArg) {
+ throw new CommandUsageException();
+ }
+ }
+
+ @Command(
+ aliases = { "npc" },
+ usage = "tfish (--body color) (--pattern pattern) (--patterncolor color)",
+ desc = "Sets tropical fish modifiers",
+ modifiers = { "tfish" },
+ min = 1,
+ max = 1,
+ permission = "citizens.npc.tropicalfish")
+ @Requirements(selected = true, ownership = true, types = EntityType.TROPICAL_FISH)
+ public void tropicalfish(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
+ TropicalFishTrait trait = npc.getTrait(TropicalFishTrait.class);
+ String output = "";
+ if (args.hasValueFlag("body")) {
+ DyeColor color = Util.matchEnum(DyeColor.values(), args.getFlag("body"));
+ if (color == null) {
+ throw new CommandException(Messages.INVALID_TROPICALFISH_COLOR,
+ Util.listValuesPretty(DyeColor.values()));
+ }
+ trait.setBodyColor(color);
+ output += Messaging.tr(Messages.TROPICALFISH_BODY_COLOR_SET, Util.prettyEnum(color));
+ }
+ if (args.hasValueFlag("patterncolor")) {
+ DyeColor color = Util.matchEnum(DyeColor.values(), args.getFlag("patterncolor"));
+ if (color == null) {
+ throw new CommandException(Messages.INVALID_TROPICALFISH_COLOR,
+ Util.listValuesPretty(DyeColor.values()));
+ }
+ trait.setPatternColor(color);
+ output += Messaging.tr(Messages.TROPICALFISH_PATTERN_COLOR_SET, Util.prettyEnum(color));
+ }
+ if (args.hasValueFlag("pattern")) {
+ Pattern pattern = Util.matchEnum(Pattern.values(), args.getFlag("pattern"));
+ if (pattern == null) {
+ throw new CommandException(Messages.INVALID_TROPICALFISH_PATTERN,
+ Util.listValuesPretty(Pattern.values()));
+ }
+ trait.setPattern(pattern);
+ output += Messaging.tr(Messages.TROPICALFISH_PATTERN_SET, Util.prettyEnum(pattern));
+ }
+ if (!output.isEmpty()) {
+ Messaging.send(sender, output);
+ } else {
+ throw new CommandUsageException();
+ }
+ }
+
+ @Command(
+ aliases = { "npc" },
+ usage = "villager (--level level) (--type type) (--profession profession)",
+ desc = "Sets villager modifiers",
+ modifiers = { "villager" },
+ min = 1,
+ max = 1,
+ permission = "citizens.npc.villager")
+ @Requirements(selected = true, ownership = true, types = EntityType.VILLAGER)
+ public void villager(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
+ VillagerTrait trait = npc.getTrait(VillagerTrait.class);
+ String output = "";
+ if (args.hasValueFlag("level")) {
+ if (args.getFlagInteger("level") < 0) {
+ throw new CommandUsageException();
+ }
+ trait.setLevel(args.getFlagInteger("level"));
+ output += Messaging.tr(Messages.VILLAGER_LEVEL_SET, args.getFlagInteger("level"));
+ }
+ if (args.hasValueFlag("type")) {
+ Villager.Type type = Util.matchEnum(Villager.Type.values(), args.getFlag("type"));
+ if (type == null) {
+ throw new CommandException(Messages.INVALID_VILLAGER_TYPE,
+ Util.listValuesPretty(Villager.Type.values()));
+ }
+ trait.setType(type);
+ output += Messaging.tr(Messages.VILLAGER_TYPE_SET, args.getFlag("type"));
+ }
+ if (args.hasValueFlag("profession")) {
+ Profession parsed = Util.matchEnum(Profession.values(), args.getFlag("profession"));
+ if (parsed == null) {
+ throw new CommandException(Messages.INVALID_PROFESSION, args.getString(1),
+ Joiner.on(',').join(Profession.values()));
+ }
+ npc.getTrait(VillagerProfession.class).setProfession(parsed);
+ output += Messaging.tr(Messages.PROFESSION_SET, npc.getName(), args.getFlag("profession"));
+ }
+ if (!output.isEmpty()) {
+ Messaging.send(sender, output);
+ } else {
+ throw new CommandUsageException();
+ }
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/FoxTrait.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/FoxTrait.java
new file mode 100644
index 000000000..a1aef55dc
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/FoxTrait.java
@@ -0,0 +1,50 @@
+package net.citizensnpcs.nms.v1_15_R1.trait;
+
+import org.bukkit.entity.Fox;
+
+import net.citizensnpcs.api.persistence.Persist;
+import net.citizensnpcs.api.trait.Trait;
+import net.citizensnpcs.api.trait.TraitName;
+
+@TraitName("foxtrait")
+public class FoxTrait extends Trait {
+ @Persist
+ private boolean crouching = false;
+ @Persist
+ private boolean sitting = false;
+ @Persist
+ private boolean sleeping = false;
+ @Persist
+ private Fox.Type type = Fox.Type.RED;
+
+ public FoxTrait() {
+ super("foxtrait");
+ }
+
+ @Override
+ public void run() {
+ if (npc.isSpawned() && npc.getEntity() instanceof Fox) {
+ Fox fox = (Fox) npc.getEntity();
+ fox.setSitting(sitting);
+ fox.setCrouching(crouching);
+ fox.setSleeping(sleeping);
+ fox.setFoxType(type);
+ }
+ }
+
+ public void setCrouching(boolean crouching) {
+ this.crouching = crouching;
+ }
+
+ public void setSitting(boolean sitting) {
+ this.sitting = sitting;
+ }
+
+ public void setSleeping(boolean sleeping) {
+ this.sleeping = sleeping;
+ }
+
+ public void setType(Fox.Type type) {
+ this.type = type;
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/LlamaTrait.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/LlamaTrait.java
new file mode 100644
index 000000000..e28111989
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/LlamaTrait.java
@@ -0,0 +1,37 @@
+package net.citizensnpcs.nms.v1_15_R1.trait;
+
+import org.bukkit.entity.Llama;
+import org.bukkit.entity.Llama.Color;
+
+import net.citizensnpcs.api.persistence.Persist;
+import net.citizensnpcs.api.trait.Trait;
+import net.citizensnpcs.api.trait.TraitName;
+
+@TraitName("llamatrait")
+public class LlamaTrait extends Trait {
+ @Persist
+ private Color color = Color.BROWN;
+ @Persist
+ private int strength = 3;
+
+ public LlamaTrait() {
+ super("llamatrait");
+ }
+
+ @Override
+ public void run() {
+ if (npc.isSpawned() && npc.getEntity() instanceof Llama) {
+ Llama llama = (Llama) npc.getEntity();
+ llama.setColor(color);
+ llama.setStrength(strength);
+ }
+ }
+
+ public void setColor(Llama.Color color) {
+ this.color = color;
+ }
+
+ public void setStrength(int strength) {
+ this.strength = strength;
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/MushroomCowTrait.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/MushroomCowTrait.java
new file mode 100644
index 000000000..0d26222cb
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/MushroomCowTrait.java
@@ -0,0 +1,34 @@
+package net.citizensnpcs.nms.v1_15_R1.trait;
+
+import org.bukkit.entity.MushroomCow;
+import org.bukkit.entity.MushroomCow.Variant;
+
+import net.citizensnpcs.api.persistence.Persist;
+import net.citizensnpcs.api.trait.Trait;
+import net.citizensnpcs.api.trait.TraitName;
+
+@TraitName("mushroomcowtrait")
+public class MushroomCowTrait extends Trait {
+ @Persist("variant")
+ private Variant variant;
+
+ public MushroomCowTrait() {
+ super("mushroomcowtrait");
+ }
+
+ @Override
+ public void onSpawn() {
+ setVariant(variant);
+ }
+
+ @Override
+ public void run() {
+ if (variant != null && npc.getEntity() instanceof MushroomCow) {
+ ((MushroomCow) npc.getEntity()).setVariant(variant);
+ }
+ }
+
+ public void setVariant(Variant variant) {
+ this.variant = variant;
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/PandaTrait.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/PandaTrait.java
new file mode 100644
index 000000000..bb0053dac
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/PandaTrait.java
@@ -0,0 +1,39 @@
+package net.citizensnpcs.nms.v1_15_R1.trait;
+
+import org.bukkit.entity.Panda;
+
+import net.citizensnpcs.api.persistence.Persist;
+import net.citizensnpcs.api.trait.Trait;
+import net.citizensnpcs.api.trait.TraitName;
+
+@TraitName("pandatrait")
+public class PandaTrait extends Trait {
+ @Persist
+ private Panda.Gene hiddenGene;
+ @Persist
+ private Panda.Gene mainGene = Panda.Gene.NORMAL;
+
+ public PandaTrait() {
+ super("pandatrait");
+ }
+
+ @Override
+ public void run() {
+ if (npc.isSpawned() && npc.getEntity() instanceof Panda) {
+ Panda panda = (Panda) npc.getEntity();
+ panda.setMainGene(mainGene);
+ if (hiddenGene != null) {
+ panda.setHiddenGene(hiddenGene);
+ }
+ }
+ }
+
+ public void setHiddenGene(Panda.Gene gene) {
+ this.hiddenGene = gene;
+ }
+
+ public void setMainGene(Panda.Gene gene) {
+ this.mainGene = gene;
+ }
+
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/ParrotTrait.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/ParrotTrait.java
new file mode 100644
index 000000000..c41fa2bcd
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/ParrotTrait.java
@@ -0,0 +1,30 @@
+package net.citizensnpcs.nms.v1_15_R1.trait;
+
+import org.bukkit.entity.Parrot;
+import org.bukkit.entity.Parrot.Variant;
+
+import net.citizensnpcs.api.persistence.Persist;
+import net.citizensnpcs.api.trait.Trait;
+import net.citizensnpcs.api.trait.TraitName;
+
+@TraitName("parrottrait")
+public class ParrotTrait extends Trait {
+ @Persist
+ private Variant variant = Variant.BLUE;
+
+ public ParrotTrait() {
+ super("parrottrait");
+ }
+
+ @Override
+ public void run() {
+ if (npc.isSpawned() && npc.getEntity() instanceof Parrot) {
+ Parrot parrot = (Parrot) npc.getEntity();
+ parrot.setVariant(variant);
+ }
+ }
+
+ public void setVariant(Parrot.Variant variant) {
+ this.variant = variant;
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/PhantomTrait.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/PhantomTrait.java
new file mode 100644
index 000000000..d74db8cc3
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/PhantomTrait.java
@@ -0,0 +1,29 @@
+package net.citizensnpcs.nms.v1_15_R1.trait;
+
+import org.bukkit.entity.Phantom;
+
+import net.citizensnpcs.api.persistence.Persist;
+import net.citizensnpcs.api.trait.Trait;
+import net.citizensnpcs.api.trait.TraitName;
+
+@TraitName("phantomtrait")
+public class PhantomTrait extends Trait {
+ @Persist
+ private int size = 1;
+
+ public PhantomTrait() {
+ super("phantomtrait");
+ }
+
+ @Override
+ public void run() {
+ if (npc.isSpawned() && npc.getEntity() instanceof Phantom) {
+ Phantom phantom = (Phantom) npc.getEntity();
+ phantom.setSize(size);
+ }
+ }
+
+ public void setSize(int size) {
+ this.size = size;
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/PufferFishTrait.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/PufferFishTrait.java
new file mode 100644
index 000000000..4e0a9a57e
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/PufferFishTrait.java
@@ -0,0 +1,29 @@
+package net.citizensnpcs.nms.v1_15_R1.trait;
+
+import org.bukkit.entity.PufferFish;
+
+import net.citizensnpcs.api.persistence.Persist;
+import net.citizensnpcs.api.trait.Trait;
+import net.citizensnpcs.api.trait.TraitName;
+
+@TraitName("pufferfishtrait")
+public class PufferFishTrait extends Trait {
+ @Persist
+ private int puffState = 1;
+
+ public PufferFishTrait() {
+ super("pufferfishtrait");
+ }
+
+ @Override
+ public void run() {
+ if (npc.isSpawned() && npc.getEntity() instanceof PufferFish) {
+ PufferFish puffer = (PufferFish) npc.getEntity();
+ puffer.setPuffState(puffState);
+ }
+ }
+
+ public void setPuffState(int state) {
+ this.puffState = state;
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/ShulkerTrait.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/ShulkerTrait.java
new file mode 100644
index 000000000..9bfc89e8c
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/ShulkerTrait.java
@@ -0,0 +1,51 @@
+package net.citizensnpcs.nms.v1_15_R1.trait;
+
+import org.bukkit.DyeColor;
+import org.bukkit.entity.Shulker;
+
+import net.citizensnpcs.api.persistence.Persist;
+import net.citizensnpcs.api.trait.Trait;
+import net.citizensnpcs.api.trait.TraitName;
+import net.citizensnpcs.nms.v1_15_R1.util.NMSImpl;
+import net.citizensnpcs.util.NMS;
+
+@TraitName("shulkertrait")
+public class ShulkerTrait extends Trait {
+ @Persist("color")
+ private DyeColor color = DyeColor.PURPLE;
+ private int lastPeekSet = 0;
+ @Persist("peek")
+ private int peek = 0;
+
+ public ShulkerTrait() {
+ super("shulkertrait");
+ }
+
+ @Override
+ public void onSpawn() {
+ setPeek(peek);
+ }
+
+ @Override
+ public void run() {
+ if (color == null) {
+ color = DyeColor.PURPLE;
+ }
+ if (npc.getEntity() instanceof Shulker) {
+ if (peek != lastPeekSet) {
+ NMS.setPeekShulker((Shulker) npc.getEntity(), peek);
+ lastPeekSet = peek;
+ }
+ NMSImpl.setShulkerColor((Shulker) npc.getEntity(), color);
+ }
+ }
+
+ public void setColor(DyeColor color) {
+ this.color = color;
+ }
+
+ public void setPeek(int peek) {
+ this.peek = peek;
+ lastPeekSet = -1;
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/TropicalFishTrait.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/TropicalFishTrait.java
new file mode 100644
index 000000000..051796ae1
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/TropicalFishTrait.java
@@ -0,0 +1,45 @@
+package net.citizensnpcs.nms.v1_15_R1.trait;
+
+import org.bukkit.DyeColor;
+import org.bukkit.entity.TropicalFish;
+import org.bukkit.entity.TropicalFish.Pattern;
+
+import net.citizensnpcs.api.persistence.Persist;
+import net.citizensnpcs.api.trait.Trait;
+import net.citizensnpcs.api.trait.TraitName;
+
+@TraitName("tropicalfishtrait")
+public class TropicalFishTrait extends Trait {
+ @Persist
+ private DyeColor bodyColor = DyeColor.BLUE;
+ @Persist
+ private Pattern pattern = Pattern.BRINELY;
+ @Persist
+ private DyeColor patternColor = DyeColor.BLUE;
+
+ public TropicalFishTrait() {
+ super("tropicalfishtrait");
+ }
+
+ @Override
+ public void run() {
+ if (npc.isSpawned() && npc.getEntity() instanceof TropicalFish) {
+ TropicalFish fish = (TropicalFish) npc.getEntity();
+ fish.setBodyColor(bodyColor);
+ fish.setPatternColor(patternColor);
+ fish.setPattern(pattern);
+ }
+ }
+
+ public void setBodyColor(DyeColor color) {
+ this.bodyColor = color;
+ }
+
+ public void setPattern(Pattern pattern) {
+ this.pattern = pattern;
+ }
+
+ public void setPatternColor(DyeColor color) {
+ this.patternColor = color;
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/VillagerTrait.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/VillagerTrait.java
new file mode 100644
index 000000000..c519cdd36
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/trait/VillagerTrait.java
@@ -0,0 +1,38 @@
+package net.citizensnpcs.nms.v1_15_R1.trait;
+
+import org.bukkit.entity.Villager;
+
+import net.citizensnpcs.api.persistence.Persist;
+import net.citizensnpcs.api.trait.Trait;
+import net.citizensnpcs.api.trait.TraitName;
+
+@TraitName("villagertrait")
+public class VillagerTrait extends Trait {
+ @Persist
+ private int level = 1;
+ @Persist
+ private Villager.Type type;
+
+ public VillagerTrait() {
+ super("villagertrait");
+ }
+
+ @Override
+ public void run() {
+ if (!(npc.getEntity() instanceof Villager))
+ return;
+ if (type != null) {
+ ((Villager) npc.getEntity()).setVillagerType(type);
+ }
+ level = Math.min(5, Math.max(1, level));
+ ((Villager) npc.getEntity()).setVillagerLevel(level);
+ }
+
+ public void setLevel(int level) {
+ this.level = level;
+ }
+
+ public void setType(Villager.Type type) {
+ this.type = type;
+ }
+}
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/util/CitizensBlockBreaker.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/util/CitizensBlockBreaker.java
new file mode 100644
index 000000000..3008810cf
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/util/CitizensBlockBreaker.java
@@ -0,0 +1,177 @@
+package net.citizensnpcs.nms.v1_15_R1.util;
+
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack;
+import org.bukkit.entity.Player;
+
+import net.citizensnpcs.api.ai.tree.BehaviorStatus;
+import net.citizensnpcs.api.npc.BlockBreaker;
+import net.citizensnpcs.api.npc.NPC;
+import net.citizensnpcs.npc.ai.NPCHolder;
+import net.citizensnpcs.util.PlayerAnimation;
+import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_15_R1.BlockPosition;
+import net.minecraft.server.v1_15_R1.Blocks;
+import net.minecraft.server.v1_15_R1.EnchantmentManager;
+import net.minecraft.server.v1_15_R1.Entity;
+import net.minecraft.server.v1_15_R1.EntityLiving;
+import net.minecraft.server.v1_15_R1.EntityPlayer;
+import net.minecraft.server.v1_15_R1.EnumItemSlot;
+import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.ItemStack;
+import net.minecraft.server.v1_15_R1.MobEffects;
+import net.minecraft.server.v1_15_R1.TagsFluid;
+import net.minecraft.server.v1_15_R1.WorldServer;
+
+public class CitizensBlockBreaker extends BlockBreaker {
+ private final BlockBreakerConfiguration configuration;
+ private int currentDamage;
+ private int currentTick;
+ private final Entity entity;
+ private boolean isDigging = true;
+ private final Location location;
+ private int startDigTick;
+ private final int x, y, z;
+
+ public CitizensBlockBreaker(org.bukkit.entity.Entity entity, org.bukkit.block.Block target,
+ BlockBreakerConfiguration config) {
+ this.entity = ((CraftEntity) entity).getHandle();
+ this.x = target.getX();
+ this.y = target.getY();
+ this.z = target.getZ();
+ this.location = target.getLocation();
+ this.startDigTick = (int) (System.currentTimeMillis() / 50);
+ this.configuration = config;
+ }
+
+ private double distanceSquared() {
+ return Math.pow(entity.locX() - x, 2) + Math.pow(entity.locY() - y, 2) + Math.pow(entity.locZ() - z, 2);
+ }
+
+ private net.minecraft.server.v1_15_R1.ItemStack getCurrentItem() {
+ return configuration.item() != null ? CraftItemStack.asNMSCopy(configuration.item())
+ : entity instanceof EntityLiving ? ((EntityLiving) entity).getEquipment(EnumItemSlot.MAINHAND) : null;
+ }
+
+ private float getStrength(IBlockData block) {
+ float base = block.getBlock().a(block, null, new BlockPosition(0, 0, 0));
+ return base < 0.0F ? 0.0F : (!isDestroyable(block) ? 1.0F / base / 100.0F : strengthMod(block) / base / 30.0F);
+ }
+
+ private boolean isDestroyable(IBlockData block) {
+ if (block.getMaterial().isAlwaysDestroyable()) {
+ return true;
+ } else {
+ ItemStack current = getCurrentItem();
+ return current != null ? current.canDestroySpecialBlock(block) : false;
+ }
+ }
+
+ @Override
+ public void reset() {
+ if (configuration.callback() != null) {
+ configuration.callback().run();
+ }
+ isDigging = false;
+ setBlockDamage(currentDamage = -1);
+ }
+
+ @Override
+ public BehaviorStatus run() {
+ if (entity.dead) {
+ return BehaviorStatus.FAILURE;
+ }
+ if (!isDigging) {
+ return BehaviorStatus.SUCCESS;
+ }
+ currentTick = (int) (System.currentTimeMillis() / 50); // CraftBukkit
+ if (configuration.radiusSquared() > 0 && distanceSquared() >= configuration.radiusSquared()) {
+ startDigTick = currentTick;
+ if (entity instanceof NPCHolder) {
+ NPC npc = ((NPCHolder) entity).getNPC();
+ if (npc != null && !npc.getNavigator().isNavigating()) {
+ npc.getNavigator()
+ .setTarget(entity.world.getWorld().getBlockAt(x, y, z).getLocation().add(0, 1, 0));
+ }
+ }
+ return BehaviorStatus.RUNNING;
+ }
+ Util.faceLocation(entity.getBukkitEntity(), location);
+ if (entity instanceof EntityPlayer) {
+ PlayerAnimation.ARM_SWING.play((Player) entity.getBukkitEntity());
+ }
+ IBlockData block = entity.world.getType(new BlockPosition(x, y, z));
+ if (block == null || block.getBlock() == Blocks.AIR) {
+ return BehaviorStatus.SUCCESS;
+ } else {
+ int tickDifference = currentTick - startDigTick;
+ float damage = getStrength(block) * (tickDifference + 1) * configuration.blockStrengthModifier();
+ if (damage >= 1F) {
+ entity.world.getWorld().getBlockAt(x, y, z)
+ .breakNaturally(CraftItemStack.asCraftMirror(getCurrentItem()));
+ return BehaviorStatus.SUCCESS;
+ }
+ int modifiedDamage = (int) (damage * 10.0F);
+ if (modifiedDamage != currentDamage) {
+ setBlockDamage(modifiedDamage);
+ currentDamage = modifiedDamage;
+ }
+ }
+ return BehaviorStatus.RUNNING;
+ }
+
+ private void setBlockDamage(int modifiedDamage) {
+ ((WorldServer) entity.world).a(entity.getId(), new BlockPosition(x, y, z), modifiedDamage); // TODO: does this
+ // work?
+ }
+
+ @Override
+ public boolean shouldExecute() {
+ return entity.world.getType(new BlockPosition(x, y, z)).getBlock() != Blocks.AIR;
+ }
+
+ private float strengthMod(IBlockData block) {
+ ItemStack itemstack = getCurrentItem();
+ float f = itemstack.a(block);
+ if (entity instanceof EntityLiving) {
+ EntityLiving handle = (EntityLiving) entity;
+ if (f > 1.0F) {
+ int i = EnchantmentManager.getDigSpeedEnchantmentLevel(handle);
+ if (i > 0) {
+ f += i * i + 1;
+ }
+ }
+ if (handle.hasEffect(MobEffects.FASTER_DIG)) {
+ f *= (1.0F + (handle.getEffect(MobEffects.FASTER_DIG).getAmplifier() + 1) * 0.2F);
+ }
+ if (handle.hasEffect(MobEffects.SLOWER_DIG)) {
+ float f1 = 1.0F;
+ switch (handle.getEffect(MobEffects.SLOWER_DIG).getAmplifier()) {
+ case 0:
+ f1 = 0.3F;
+ break;
+ case 1:
+ f1 = 0.09F;
+ break;
+ case 2:
+ f1 = 0.0027F;
+ break;
+ case 3:
+ default:
+ f1 = 8.1E-4F;
+ }
+ f *= f1;
+ }
+
+ if (handle.a(TagsFluid.WATER) && !EnchantmentManager.h(handle)) {
+ f /= 5.0F;
+ }
+
+ }
+ if (!entity.onGround) {
+ f /= 5.0F;
+ }
+ return f;
+ }
+}
\ No newline at end of file
diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/util/CustomEntityRegistry.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/util/CustomEntityRegistry.java
new file mode 100644
index 000000000..eb777288f
--- /dev/null
+++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/util/CustomEntityRegistry.java
@@ -0,0 +1,211 @@
+package net.citizensnpcs.nms.v1_15_R1.util;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Random;
+import java.util.Set;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.Maps;
+
+import net.minecraft.server.v1_15_R1.*;
+
+@SuppressWarnings("rawtypes")
+public class CustomEntityRegistry extends RegistryBlocks {
+ private final BiMap entities = HashBiMap.create();
+ private final BiMap entityClasses = this.entities.inverse();
+ private final Map entityIds = Maps.newHashMap();
+ private final RegistryMaterials> wrapped;
+
+ public CustomEntityRegistry(RegistryBlocks> original) {
+ super(original.a().getNamespace());
+ this.wrapped = original;
+ }
+
+ @Override
+ public int a(Object key) {
+ if (entityIds.containsKey(key)) {
+ return entityIds.get(key);
+ }
+
+ return wrapped.a((EntityTypes) key);
+ }
+
+ @Override
+ public Object a(Random paramRandom) {
+ return wrapped.a(paramRandom);
+ }
+
+ public EntityTypes findType(Class> search) {
+ return minecraftClassMap.inverse().get(search);
+ /*
+ for (Object type : wrapped) {
+ if (minecraftClassMap.get(type) == search) {
+ return (EntityTypes) type;
+ }
+ }
+ return null;
+ */
+ }
+
+ @Override
+ public Object fromId(int var0) {
+ return this.wrapped.fromId(var0);
+ }
+
+ @Override
+ public EntityTypes get(MinecraftKey key) {
+ if (entities.containsKey(key)) {
+ return entities.get(key);
+ }
+
+ return wrapped.get(key);
+ }
+
+ @Override
+ public MinecraftKey getKey(Object value) {
+ if (entityClasses.containsKey(value)) {
+ return entityClasses.get(value);
+ }
+
+ return wrapped.getKey((EntityTypes) value);
+ }
+
+ @Override
+ public Optional getOptional(MinecraftKey var0) {
+ if (entities.containsKey(var0)) {
+ return Optional.of(entities.get(var0));
+ }
+
+ return Optional.ofNullable(this.wrapped.get(var0));
+ }
+
+ public RegistryMaterials> getWrapped() {
+ return wrapped;
+ }
+
+ @Override
+ public Iterator