From e8c57acac14e9ffc322f99469b15bcc2800b91b2 Mon Sep 17 00:00:00 2001
From: fullwall <fullwall25@gmail.com>
Date: Mon, 27 Jul 2020 10:44:13 +0800
Subject: [PATCH] Add boat movement to 1.16, include ID in default selection
 message

---
 .../main/java/net/citizensnpcs/Settings.java  |   2 +-
 .../citizensnpcs/commands/NPCCommands.java    |   2 +-
 .../net/citizensnpcs/trait/Controllable.java  |   3 +-
 .../java/net/citizensnpcs/util/Messages.java  |   1 +
 .../src/main/resources/messages_en.properties |   1 +
 .../entity/nonliving/BoatController.java      | 161 ++++++++++++++++--
 6 files changed, 151 insertions(+), 19 deletions(-)

diff --git a/main/src/main/java/net/citizensnpcs/Settings.java b/main/src/main/java/net/citizensnpcs/Settings.java
index ea6a9c0bc..c4d9441eb 100644
--- a/main/src/main/java/net/citizensnpcs/Settings.java
+++ b/main/src/main/java/net/citizensnpcs/Settings.java
@@ -127,7 +127,7 @@ public class Settings {
         REMOVE_PLAYERS_FROM_PLAYER_LIST("npc.player.remove-from-list", true),
         SAVE_TASK_DELAY("storage.save-task.delay", 20 * 60 * 60),
         SELECTION_ITEM("npc.selection.item", "stick"),
-        SELECTION_MESSAGE("npc.selection.message", "<b>You selected <a><npc><b>!"),
+        SELECTION_MESSAGE("npc.selection.message", "Selected [[<npc>]] (ID <id>)."),
         SERVER_OWNS_NPCS("npc.server-ownership", false),
         STORAGE_FILE("storage.file", "saves.yml"),
         STORAGE_TYPE("storage.type", "yaml"),
diff --git a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java
index 4c463227a..2c05fafe4 100644
--- a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java
+++ b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java
@@ -1122,7 +1122,7 @@ public class NPCCommands {
                 throw new CommandException(Messaging.tr(Messages.MOUNT_NPC_MUST_BE_SPAWNED, args.getFlag("onnpc")));
             }
             if (mount.equals(npc)) {
-                throw new CommandException();
+                throw new CommandException(Messages.TRIED_TO_MOUNT_NPC_ON_ITSELF);
             }
             NMS.mount(mount.getEntity(), npc.getEntity());
             return;
diff --git a/main/src/main/java/net/citizensnpcs/trait/Controllable.java b/main/src/main/java/net/citizensnpcs/trait/Controllable.java
index 3072de68c..6a405d8b0 100644
--- a/main/src/main/java/net/citizensnpcs/trait/Controllable.java
+++ b/main/src/main/java/net/citizensnpcs/trait/Controllable.java
@@ -318,7 +318,8 @@ public class Controllable extends Trait implements Toggleable, CommandConfigurab
             boolean onGround = NMS.isOnGround(npc.getEntity());
             float speedMod = npc.getNavigator().getDefaultParameters()
                     .modifiedSpeed((onGround ? GROUND_SPEED : AIR_SPEED));
-            if (!Util.isHorse(npc.getEntity().getType())) { // just use minecraft horse physics
+            if (!Util.isHorse(npc.getEntity().getType())) {
+                // just use minecraft horse physics
                 speed = updateHorizontalSpeed(npc.getEntity(), rider, speed, speedMod);
             }
 
diff --git a/main/src/main/java/net/citizensnpcs/util/Messages.java b/main/src/main/java/net/citizensnpcs/util/Messages.java
index c907ee650..ee004913e 100644
--- a/main/src/main/java/net/citizensnpcs/util/Messages.java
+++ b/main/src/main/java/net/citizensnpcs/util/Messages.java
@@ -356,6 +356,7 @@ public class Messages {
     public static final String TRAITS_FAILED_TO_ADD = "citizens.commands.trait.failed-to-add";
     public static final String TRAITS_FAILED_TO_CHANGE = "citizens.commands.trait.failed-to-change";
     public static final String TRAITS_REMOVED = "citizens.commands.trait.removed";
+    public static final String TRIED_TO_MOUNT_NPC_ON_ITSELF = "citizens.commands.npc.mount.mount-on-itself";
     public static final String TROPICALFISH_BODY_COLOR_SET = "citizens.commands.npc.tropicalfish.body-color-set";
     public static final String TROPICALFISH_PATTERN_COLOR_SET = "citizens.commands.npc.tropicalfish.pattern-color-set";
     public static final String TROPICALFISH_PATTERN_SET = "citizens.commands.npc.tropicalfish.pattern-set";
diff --git a/main/src/main/resources/messages_en.properties b/main/src/main/resources/messages_en.properties
index d89bda510..4ef687534 100644
--- a/main/src/main/resources/messages_en.properties
+++ b/main/src/main/resources/messages_en.properties
@@ -128,6 +128,7 @@ citizens.commands.npc.minecart.set=[[{0}]] now has item [[{1}]]:[[{2}]] with off
 citizens.commands.npc.mount.failed=Couldn''t mount [[{0}]].
 citizens.commands.npc.mount.must-be-spawned=Couldn''t mount [[{0}]]. Make sure that the destination NPC ID is correct and it is spawned.
 citizens.commands.npc.moveto.format=Format is x:y:z(:world) or x y z( world).
+citizens.commands.npc.mount.mount-on-itself=Can''t mount NPC on itself.
 citizens.commands.npc.moveto.teleported=[[{0}]] teleported to [[{1}]].
 citizens.commands.npc.mushroomcow.invalid-variant=Invalid variant. Valid values are: [[{0}]].
 citizens.commands.npc.mushroomcow.variant-set=[[{0}]]''s variant set to [[{1}]].
diff --git a/v1_16_R1/src/main/java/net/citizensnpcs/nms/v1_16_R1/entity/nonliving/BoatController.java b/v1_16_R1/src/main/java/net/citizensnpcs/nms/v1_16_R1/entity/nonliving/BoatController.java
index e514cd9d8..08834f682 100644
--- a/v1_16_R1/src/main/java/net/citizensnpcs/nms/v1_16_R1/entity/nonliving/BoatController.java
+++ b/v1_16_R1/src/main/java/net/citizensnpcs/nms/v1_16_R1/entity/nonliving/BoatController.java
@@ -14,9 +14,17 @@ import net.citizensnpcs.nms.v1_16_R1.util.NMSImpl;
 import net.citizensnpcs.npc.CitizensNPC;
 import net.citizensnpcs.npc.ai.NPCHolder;
 import net.citizensnpcs.util.Util;
+import net.minecraft.server.v1_16_R1.AxisAlignedBB;
+import net.minecraft.server.v1_16_R1.BlockPosition;
 import net.minecraft.server.v1_16_R1.EntityBoat;
+import net.minecraft.server.v1_16_R1.EntityHuman;
 import net.minecraft.server.v1_16_R1.EntityTypes;
+import net.minecraft.server.v1_16_R1.EnumMoveType;
+import net.minecraft.server.v1_16_R1.Fluid;
+import net.minecraft.server.v1_16_R1.MathHelper;
 import net.minecraft.server.v1_16_R1.NBTTagCompound;
+import net.minecraft.server.v1_16_R1.TagsFluid;
+import net.minecraft.server.v1_16_R1.Vec3D;
 import net.minecraft.server.v1_16_R1.World;
 
 public class BoatController extends MobEntityController {
@@ -44,6 +52,12 @@ public class BoatController extends MobEntityController {
     }
 
     public static class EntityBoatNPC extends EntityBoat implements NPCHolder {
+        private double aC;
+        private float aD;
+        private EnumStatus aE;
+        private EnumStatus aF;
+        private double ap;
+        private double ar;
         private final CitizensNPC npc;
 
         public EntityBoatNPC(EntityTypes<? extends EntityBoat> types, World world) {
@@ -70,6 +84,35 @@ public class BoatController extends MobEntityController {
             return npc == null ? super.d(save) : false;
         }
 
+        @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;
+        }
+
+        private EnumStatus getStatus() {
+            EnumStatus entityboat_enumstatus = u();
+            if (entityboat_enumstatus != null) {
+                this.aC = (getBoundingBox()).maxY;
+                return entityboat_enumstatus;
+            }
+            if (t())
+                return EnumStatus.IN_WATER;
+            float f = k();
+            if (f > 0.0F) {
+                this.aD = f;
+                return EnumStatus.ON_LAND;
+            }
+            return EnumStatus.IN_AIR;
+        }
+
         @Override
         public void h(double x, double y, double z) {
             if (npc == null) {
@@ -92,17 +135,112 @@ public class BoatController extends MobEntityController {
             // cancelled.
         }
 
-        @Override
-        public CraftEntity getBukkitEntity() {
-            if (npc != null && !(super.getBukkitEntity() instanceof NPCHolder)) {
-                NMSImpl.setBukkitEntity(this, new BoatNPC(this));
+        private boolean t() {
+            boolean m = false;
+            AxisAlignedBB axisalignedbb = getBoundingBox();
+            int i = MathHelper.floor(axisalignedbb.minX);
+            int j = MathHelper.f(axisalignedbb.maxX);
+            int k = MathHelper.floor(axisalignedbb.minY);
+            int l = MathHelper.f(axisalignedbb.minY + 0.001D);
+            int i1 = MathHelper.floor(axisalignedbb.minZ);
+            int j1 = MathHelper.f(axisalignedbb.maxZ);
+            boolean flag = false;
+            this.aC = Double.MIN_VALUE;
+            BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();
+            for (int k1 = i; k1 < j; k1++) {
+                for (int l1 = k; l1 < l; l1++) {
+                    for (int i2 = i1; i2 < j1; i2++) {
+                        blockposition_mutableblockposition.d(k1, l1, i2);
+                        Fluid fluid = this.world.getFluid(blockposition_mutableblockposition);
+                        if (fluid.a(TagsFluid.WATER)) {
+                            float f = l1 + fluid.getHeight(this.world, blockposition_mutableblockposition);
+                            this.aC = Math.max(f, this.aC);
+                            m = flag | ((axisalignedbb.minY < f) ? true : false);
+                        }
+                    }
+                }
             }
-            return super.getBukkitEntity();
+            return m;
         }
 
         @Override
-        public NPC getNPC() {
-            return npc;
+        public void tick() {
+            if (npc != null) {
+                npc.update();
+                this.aF = this.aE;
+                aE = getStatus();
+                double d1 = isNoGravity() ? 0.0D : -0.04D;
+                double d2 = 0.0D;
+                this.ap = 0.05F;
+                if (this.aF == EnumStatus.IN_AIR && this.aE != EnumStatus.IN_AIR && this.aE != EnumStatus.ON_LAND) {
+                    this.aC = e(1.0D);
+                    setPosition(locX(), (i() - getHeight()) + 0.101D, locZ());
+                    setMot(getMot().d(1.0D, 0.0D, 1.0D));
+                    this.aE = EnumStatus.IN_WATER;
+                } else {
+                    if (this.aE == EnumStatus.IN_WATER) {
+                        d2 = (this.aC - locY()) / getHeight();
+                        this.ap = 0.9F;
+                    } else if (this.aE == EnumStatus.UNDER_FLOWING_WATER) {
+                        d1 = -7.0E-4D;
+                        this.ap = 0.9F;
+                    } else if (this.aE == EnumStatus.UNDER_WATER) {
+                        d2 = 0.01D;
+                        this.ap = 0.45F;
+                    } else if (this.aE == EnumStatus.IN_AIR) {
+                        this.ap = 0.9F;
+                    } else if (this.aE == EnumStatus.ON_LAND) {
+                        this.ap = this.aD;
+                        if (getRidingPassenger() instanceof EntityHuman) {
+                            this.aD /= 2.0F;
+                        }
+                    }
+                    Vec3D vec3d = getMot();
+                    setMot(vec3d.x * this.ap, vec3d.y + d1, vec3d.z * this.ap);
+                    this.ar *= this.ap;
+                    if (d2 > 0.0D) {
+                        Vec3D vec3d1 = getMot();
+                        setMot(vec3d1.x, (vec3d1.y + d2 * 0.0615D), vec3d1.z);
+                    }
+                }
+                move(EnumMoveType.SELF, getMot());
+                if (isVehicle()) {
+                    float f = 0.0F;
+                    this.yaw += this.ar;
+                    setMot(getMot().add((MathHelper.sin(-this.yaw * 0.017453292F) * f), 0.0D,
+                            (MathHelper.cos(this.yaw * 0.017453292F) * f)));
+                }
+            } else {
+                super.tick();
+            }
+        }
+
+        private EnumStatus u() {
+            AxisAlignedBB axisalignedbb = getBoundingBox();
+            double d0 = axisalignedbb.maxY + 0.001D;
+            int i = MathHelper.floor(axisalignedbb.minX);
+            int j = MathHelper.f(axisalignedbb.maxX);
+            int k = MathHelper.floor(axisalignedbb.maxY);
+            int l = MathHelper.f(d0);
+            int i1 = MathHelper.floor(axisalignedbb.minZ);
+            int j1 = MathHelper.f(axisalignedbb.maxZ);
+            boolean flag = false;
+            BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();
+            for (int k1 = i; k1 < j; k1++) {
+                for (int l1 = k; l1 < l; l1++) {
+                    for (int i2 = i1; i2 < j1; i2++) {
+                        blockposition_mutableblockposition.d(k1, l1, i2);
+                        Fluid fluid = this.world.getFluid(blockposition_mutableblockposition);
+                        if (fluid.a(TagsFluid.WATER) && d0 < (blockposition_mutableblockposition.getY()
+                                + fluid.getHeight(this.world, blockposition_mutableblockposition))) {
+                            if (!fluid.isSource())
+                                return EnumStatus.UNDER_FLOWING_WATER;
+                            flag = true;
+                        }
+                    }
+                }
+            }
+            return flag ? EnumStatus.UNDER_WATER : null;
         }
 
         @Override
@@ -113,14 +251,5 @@ public class BoatController extends MobEntityController {
                 NMSImpl.setSize(this, justCreated);
             }
         }
-
-        @Override
-        public void tick() {
-            if (npc != null) {
-                npc.update();
-            } else {
-                super.tick();
-            }
-        }
     }
 }