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/core/BlockPosition.java b/src/main/java/net/minecraft/core/BlockPosition.java
index 8edc279e7a3fdfb7e10718f1deee34b7e3fb2f28..a01b69c43acf4beb65c8b7a8a800c44121068d46 100644
--- a/src/main/java/net/minecraft/core/BlockPosition.java
+++ b/src/main/java/net/minecraft/core/BlockPosition.java
@@ -11,6 +11,7 @@ import java.util.stream.StreamSupport;
 import javax.annotation.concurrent.Immutable;
 import net.minecraft.SystemUtils;
 import net.minecraft.util.MathHelper;
+import net.minecraft.world.entity.Entity;
 import net.minecraft.world.level.block.EnumBlockRotation;
 import net.minecraft.world.level.levelgen.structure.StructureBoundingBox;
 import net.minecraft.world.phys.AxisAlignedBB;
@@ -42,6 +43,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/level/EntityPlayer.java b/src/main/java/net/minecraft/server/level/EntityPlayer.java
index ba71636527f173dfc5ddae9ef0f7c73d0423d9b3..c2e987808ae5ccb23cc7bcc5221faea11153bc7f 100644
--- a/src/main/java/net/minecraft/server/level/EntityPlayer.java
+++ b/src/main/java/net/minecraft/server/level/EntityPlayer.java
@@ -106,6 +106,7 @@ import net.minecraft.world.entity.monster.EntityMonster;
 import net.minecraft.world.entity.player.EntityHuman;
 import net.minecraft.world.entity.player.EnumChatVisibility;
 import net.minecraft.world.entity.projectile.EntityArrow;
+import net.minecraft.world.entity.vehicle.EntityMinecartAbstract;
 import net.minecraft.world.inventory.Container;
 import net.minecraft.world.inventory.ContainerHorse;
 import net.minecraft.world.inventory.ICrafting;
@@ -1132,6 +1133,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/world/entity/EntityLiving.java b/src/main/java/net/minecraft/world/entity/EntityLiving.java
index 5cdefe2a1b4085e3aae7dbbb751cfd368593ebd7..f1781c2100bf3a8fa7123b66f9ab682177d6490e 100644
--- a/src/main/java/net/minecraft/world/entity/EntityLiving.java
+++ b/src/main/java/net/minecraft/world/entity/EntityLiving.java
@@ -194,9 +194,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/world/entity/vehicle/EntityMinecartAbstract.java b/src/main/java/net/minecraft/world/entity/vehicle/EntityMinecartAbstract.java
index 75a88ab5d5b0fdb98ea8d61bb6b82049b21101f3..3cd9a7956f9cbb3f66764adfede12b3b25024c41 100644
--- a/src/main/java/net/minecraft/world/entity/vehicle/EntityMinecartAbstract.java
+++ b/src/main/java/net/minecraft/world/entity/vehicle/EntityMinecartAbstract.java
@@ -484,16 +484,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.getMaterial().isSolid()) {
+            block = world.getType(position.shift(EnumDirection.DOWN)).getBlock();
+        }
+        Double speed = world.purpurConfig.controllableMinecartsBlockSpeeds.get(block);
+        if (!block.getMaterial().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/world/item/ItemMinecart.java b/src/main/java/net/minecraft/world/item/ItemMinecart.java
index 527f3ed664854cdd938c34f00a064bc2f77148cc..d50a1093aa9b6d7187b59566309a3abd05125e07 100644
--- a/src/main/java/net/minecraft/world/item/ItemMinecart.java
+++ b/src/main/java/net/minecraft/world/item/ItemMinecart.java
@@ -121,8 +121,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) {
@@ -149,6 +151,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/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java
index f8a982add50862f1bc977f3039e7e9aeed9138ae..e18b3b575e4fe3fde869e749070feefb31548a25 100644
--- a/src/main/java/net/minecraft/world/level/block/Block.java
+++ b/src/main/java/net/minecraft/world/level/block/Block.java
@@ -41,6 +41,7 @@ import net.minecraft.world.level.block.entity.TileEntity;
 import net.minecraft.world.level.block.state.BlockBase;
 import net.minecraft.world.level.block.state.BlockStateList;
 import net.minecraft.world.level.block.state.IBlockData;
+import net.minecraft.world.level.material.Material;
 import net.minecraft.world.level.storage.loot.LootTableInfo;
 import net.minecraft.world.level.storage.loot.parameters.LootContextParameters;
 import net.minecraft.world.phys.Vec3D;
@@ -83,6 +84,7 @@ public class Block extends BlockBase implements IMaterial {
         return timing;
     }
     // Paper end
+    public Material getMaterial() { return material; } // Purpur - OBFHELPER
     @Nullable
     private String name;
     @Nullable
diff --git a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
index 2e4a928a3e88fb3d961d9530695cd7154321c79b..3a6a19bd4e952a6deb1def80755387cab75bbad8 100644
--- a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
+++ b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
@@ -1,13 +1,18 @@
 package net.pl3x.purpur;
 
 import net.minecraft.core.IRegistry;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.Blocks;
 import net.minecraft.world.item.Item;
 import net.minecraft.world.item.Items;
 import net.minecraft.resources.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;
