From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
Date: Sat, 29 Jun 2019 02:32:40 -0500
Subject: [PATCH] Controllable Minecarts


diff --git a/src/main/java/net/minecraft/server/BlockPosition.java b/src/main/java/net/minecraft/server/BlockPosition.java
index 2291135eaef64c403183724cb6e413cd7e472672..bc61aaff65a7dc1e7534452b285953b83adb7000 100644
--- a/src/main/java/net/minecraft/server/BlockPosition.java
+++ b/src/main/java/net/minecraft/server/BlockPosition.java
@@ -36,6 +36,12 @@ public class BlockPosition extends BaseBlockPosition {
     private static final int m = 38;
     // Paper end
 
+    // Purpur start
+    public BlockPosition(Entity entity) {
+        super(entity.locX(), entity.locY(), entity.locZ());
+    }
+    // Purpur end
+
     public BlockPosition(int i, int j, int k) {
         super(i, j, k);
     }
diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java
index 8cc440e0e7108e2c7ca29b2f8a2fc5a723dcef0d..46b1bc644c0584fe7006ea9f111d7f4837299dc9 100644
--- a/src/main/java/net/minecraft/server/EntityLiving.java
+++ b/src/main/java/net/minecraft/server/EntityLiving.java
@@ -99,9 +99,9 @@ public abstract class EntityLiving extends Entity {
     protected int aO;protected int getKillCount() { return this.aO; } // Paper - OBFHELPER
     public float lastDamage;
     public boolean jumping; // Paper protected -> public
-    public float aR;
-    public float aS;
-    public float aT;
+    public float aR; public float getStrafe() { return aR; } public void setStrafe(float strafe) { aR = strafe; } // Purpur - OBFHELPER
+    public float aS; public float getVertical() { return aS; } public void setVertical(float vertical) { aS = vertical; } // Purpur - OBFHELPER
+    public float aT; public float getForward() { return aT; } public void setForward(float forward) { aT = forward; } // Purpur - OBFHELPER
     protected int aU;
     protected double aV;
     protected double aW;
diff --git a/src/main/java/net/minecraft/server/EntityMinecartAbstract.java b/src/main/java/net/minecraft/server/EntityMinecartAbstract.java
index be859a1b41254b299a507d03e453dc8efee6f3dd..4de2877f30a9b231a5c8bbd1739416991e9d7633 100644
--- a/src/main/java/net/minecraft/server/EntityMinecartAbstract.java
+++ b/src/main/java/net/minecraft/server/EntityMinecartAbstract.java
@@ -445,16 +445,62 @@ public abstract class EntityMinecartAbstract extends Entity {
 
     public void a(int i, int j, int k, boolean flag) {}
 
+    // Purpur start
+    private Double lastSpeed;
+
+    public double getControllableSpeed() {
+        BlockPosition position = new BlockPosition(this);
+        Block block = world.getType(position).getBlock();
+        if (!block.material.isSolid()) {
+            block = world.getType(position.shift(EnumDirection.DOWN)).getBlock();
+        }
+        Double speed = world.purpurConfig.controllableMinecartsBlockSpeeds.get(block);
+        if (!block.material.isSolid()) {
+            speed = lastSpeed;
+        } else if (speed == null) {
+            speed = world.purpurConfig.controllableMinecartsBaseSpeed;
+        }
+        return lastSpeed = speed;
+    }
+    // Purpur end
+
     protected void h() {
         double d0 = this.getMaxSpeed();
         Vec3D vec3d = this.getMot();
 
         this.setMot(MathHelper.a(vec3d.x, -d0, d0), vec3d.y, MathHelper.a(vec3d.z, -d0, d0));
+
+        // Purpur start
+        if (world.purpurConfig.controllableMinecarts && !isInWater() && !isInLava() && !passengers.isEmpty()) {
+            Entity passenger = passengers.get(0);
+            if (passenger instanceof EntityHuman) {
+                EntityHuman entityhuman = (EntityHuman) passenger;
+                if (entityhuman.jumping && this.onGround) {
+                    setMot(new Vec3D(getMot().x, world.purpurConfig.controllableMinecartsHopBoost, getMot().z));
+                }
+                if (entityhuman.getForward() != 0.0F) {
+                    Vector velocity = entityhuman.getBukkitEntity().getEyeLocation().getDirection().normalize().multiply(getControllableSpeed());
+                    if (entityhuman.getForward() < 0.0) {
+                        velocity.multiply(-0.5);
+                    }
+                    setMot(new Vec3D(velocity.getX(), getMot().y, velocity.getZ()));
+                }
+                this.yaw = passenger.yaw - 90;
+                setStepHeight(world.purpurConfig.controllableMinecartsStepHeight);
+            } else {
+                setStepHeight(0.0F);
+            }
+        } else {
+            setStepHeight(0.0F);
+        }
+        // Purpur end
+
         if (this.onGround) {
             // CraftBukkit start - replace magic numbers with our variables
             this.setMot(new Vec3D(this.getMot().x * this.derailedX, this.getMot().y * this.derailedY, this.getMot().z * this.derailedZ));
             // CraftBukkit end
         }
+        else if (world.purpurConfig.controllableMinecarts) setMot(new Vec3D(getMot().x * derailedX, getMot().y, getMot().z * derailedZ)); // Purpur
 
         this.move(EnumMoveType.SELF, this.getMot());
         if (!this.onGround) {
diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java
index cdc559a8330d804a269f942f5bcba84ab9ea4289..320719bcc43b69a4dcca53d431a1eb4af0034c71 100644
--- a/src/main/java/net/minecraft/server/EntityPlayer.java
+++ b/src/main/java/net/minecraft/server/EntityPlayer.java
@@ -1002,6 +1002,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
         if (this.isInvulnerable(damagesource)) {
             return false;
         } else {
+            if (damagesource == DamageSource.FALL && getRootVehicle() instanceof EntityMinecartAbstract && world.purpurConfig.controllableMinecarts && !world.purpurConfig.controllableMinecartsFallDamage) return false; // Purpur
             boolean flag = this.server.j() && this.canPvP() && "fall".equals(damagesource.translationIndex);
 
             if (!flag && isSpawnInvulnerable() && damagesource != DamageSource.OUT_OF_WORLD) { // Purpur
diff --git a/src/main/java/net/minecraft/server/ItemMinecart.java b/src/main/java/net/minecraft/server/ItemMinecart.java
index ceef7aaf923026ff1044d6feba4297279eb44157..002651aaf3b8a9b489fe323756cd1ad13f9874e8 100644
--- a/src/main/java/net/minecraft/server/ItemMinecart.java
+++ b/src/main/java/net/minecraft/server/ItemMinecart.java
@@ -103,8 +103,10 @@ public class ItemMinecart extends Item {
         IBlockData iblockdata = world.getType(blockposition);
 
         if (!iblockdata.a((Tag) TagsBlock.RAILS)) {
-            return EnumInteractionResult.FAIL;
-        } else {
+            // Purpur start - place minecarts anywhere
+            if (!world.purpurConfig.controllableMinecartsPlaceAnywhere) return EnumInteractionResult.FAIL;
+            if (iblockdata.getMaterial().isSolid()) blockposition = blockposition.shift(itemactioncontext.getClickedFace());
+        } //else { // Purpur end - place minecarts anywhere
             ItemStack itemstack = itemactioncontext.getItemStack();
 
             if (!world.isClientSide) {
@@ -131,6 +133,6 @@ public class ItemMinecart extends Item {
 
             itemstack.subtract(1);
             return EnumInteractionResult.a(world.isClientSide);
-        }
+        //} // Purpur - place minecarts anywhere
     }
 }
diff --git a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
index 56793a9e8f963b63a22aac5337299fbfb07389f0..c6682064ff5d653d14f658396a6a5461d4554fab 100644
--- a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
+++ b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
@@ -1,5 +1,7 @@
 package net.pl3x.purpur;
 
+import net.minecraft.server.Block;
+import net.minecraft.server.Blocks;
 import net.minecraft.server.IRegistry;
 import net.minecraft.server.Item;
 import net.minecraft.server.Items;
@@ -7,7 +9,10 @@ import net.minecraft.server.MinecraftKey;
 import org.bukkit.configuration.ConfigurationSection;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+
 import static net.pl3x.purpur.PurpurConfig.log;
 
 public class PurpurWorldConfig {
@@ -68,6 +73,34 @@ public class PurpurWorldConfig {
         armorstandStepHeight = (float) getDouble("gameplay-mechanics.armorstand.step-height", armorstandStepHeight);
     }
 
+    public boolean controllableMinecarts = false;
+    public boolean controllableMinecartsPlaceAnywhere = false;
+    public float controllableMinecartsStepHeight = 1.0F;
+    public double controllableMinecartsHopBoost = 0.5D;
+    public boolean controllableMinecartsFallDamage = true;
+    public double controllableMinecartsBaseSpeed = 0.1D;
+    public Map<Block, Double> controllableMinecartsBlockSpeeds = new HashMap<>();
+    private void controllableMinecartsSettings() {
+        controllableMinecarts = getBoolean("gameplay-mechanics.controllable-minecarts.enabled", controllableMinecarts);
+        controllableMinecartsPlaceAnywhere = getBoolean("gameplay-mechanics.controllable-minecarts.place-anywhere", controllableMinecartsPlaceAnywhere);
+        controllableMinecartsStepHeight = (float) getDouble("gameplay-mechanics.controllable-minecarts.step-height", controllableMinecartsStepHeight);
+        controllableMinecartsHopBoost = getDouble("gameplay-mechanics.controllable-minecarts.hop-boost", controllableMinecartsHopBoost);
+        controllableMinecartsFallDamage = getBoolean("gameplay-mechanics.controllable-minecarts.fall-damage", controllableMinecartsFallDamage);
+        controllableMinecartsBaseSpeed = getDouble("gameplay-mechanics.controllable-minecarts.base-speed", controllableMinecartsBaseSpeed);
+        ConfigurationSection section = getConfigurationSection("gameplay-mechanics.controllable-minecarts.block-speed");
+        if (section != null) {
+            for (String key : section.getKeys(false)) {
+                Block block = IRegistry.BLOCK.get(new MinecraftKey(key));
+                if (block != Blocks.AIR) {
+                    controllableMinecartsBlockSpeeds.put(block, section.getDouble(key, controllableMinecartsBaseSpeed));
+                }
+            }
+        } else {
+            set("gameplay-mechanics.controllable-minecarts.block-speed.grass-block", 0.3D);
+            set("gameplay-mechanics.controllable-minecarts.block-speed.stone", 0.5D);
+        }
+    }
+
     public boolean idleTimeoutKick = true;
     public boolean idleTimeoutTickNearbyEntities = true;
     public boolean idleTimeoutCountAsSleeping = false;
