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] Minecart settings and WASD controls


diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index b13328e1563fa01c141a52bf898ebc520098d0d2..b9f7812f19b194dc19b63cabd9369c5ed56dbe73 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -991,6 +991,7 @@ public class ServerPlayer extends Player {
         if (this.isInvulnerableTo(source)) {
             return false;
         } else {
+            if (source == DamageSource.FALL && getRootVehicle() instanceof net.minecraft.world.entity.vehicle.AbstractMinecart && level.purpurConfig.minecartControllable && !level.purpurConfig.minecartControllableFallDamage) return false; // Purpur
             boolean flag = this.server.isDedicatedServer() && this.isPvpAllowed() && "fall".equals(source.msgId);
 
             if (!flag && isSpawnInvulnerable() && source != DamageSource.OUT_OF_WORLD) { // Purpur
diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java
index 309bafd257d4932cfd69c2c212b32306938cd234..1e3077a22d9d3d26356b865001dcce81c9c1d7e5 100644
--- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java
+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java
@@ -110,11 +110,13 @@ public abstract class AbstractMinecart extends Entity {
     private double flyingY = 0.949999988079071D; // Paper - restore vanilla precision
     private double flyingZ = 0.949999988079071D; // Paper - restore vanilla precision
     public double maxSpeed = 0.4D;
+    public double storedMaxSpeed; // Purpur
     // CraftBukkit end
 
     protected AbstractMinecart(EntityType<?> type, Level world) {
         super(type, world);
         this.blocksBuilding = true;
+        if (world != null) maxSpeed = storedMaxSpeed = world.purpurConfig.minecartMaxSpeed; // Purpur
     }
 
     protected AbstractMinecart(EntityType<?> type, Level world, double x, double y, double z) {
@@ -327,6 +329,12 @@ public abstract class AbstractMinecart extends Entity {
 
     @Override
     public void tick() {
+        // Purpur start
+        if (storedMaxSpeed != level.purpurConfig.minecartMaxSpeed) {
+            maxSpeed = storedMaxSpeed = level.purpurConfig.minecartMaxSpeed;
+        }
+        // Purpur end
+
         // CraftBukkit start
         double prevX = this.getX();
         double prevY = this.getY();
@@ -490,16 +498,62 @@ public abstract class AbstractMinecart extends Entity {
 
     public void activateMinecart(int x, int y, int z, boolean powered) {}
 
+    // Purpur start
+    private Double lastSpeed;
+
+    public double getControllableSpeed() {
+        BlockPos pos = new BlockPos(this);
+        Block block = level.getBlockState(pos).getBlock();
+        if (!block.material.isSolid()) {
+            block = level.getBlockState(pos.relative(Direction.DOWN)).getBlock();
+        }
+        Double speed = level.purpurConfig.minecartControllableBlockSpeeds.get(block);
+        if (!block.material.isSolid()) {
+            speed = lastSpeed;
+        } else if (speed == null) {
+            speed = level.purpurConfig.minecartControllableBaseSpeed;
+        }
+        return lastSpeed = speed;
+    }
+    // Purpur end
+
     protected void comeOffTrack() {
         double d0 = this.getMaxSpeed();
         Vec3 vec3d = this.getDeltaMovement();
 
         this.setDeltaMovement(Mth.clamp(vec3d.x, -d0, d0), vec3d.y, Mth.clamp(vec3d.z, -d0, d0));
+
+        // Purpur start
+        if (level.purpurConfig.minecartControllable && !isInWater() && !isInLava() && !passengers.isEmpty()) {
+            Entity passenger = passengers.get(0);
+            if (passenger instanceof Player) {
+                Player player = (Player) passenger;
+                if (player.jumping && this.onGround) {
+                    setDeltaMovement(new Vec3(getDeltaMovement().x, level.purpurConfig.minecartControllableHopBoost, getDeltaMovement().z));
+                }
+                if (player.zza != 0.0F) {
+                    Vector velocity = player.getBukkitEntity().getEyeLocation().getDirection().normalize().multiply(getControllableSpeed());
+                    if (player.zza < 0.0) {
+                        velocity.multiply(-0.5);
+                    }
+                    setDeltaMovement(new Vec3(velocity.getX(), getDeltaMovement().y, velocity.getZ()));
+                }
+                this.setYRot(passenger.getYRot() - 90);
+                maxUpStep = level.purpurConfig.minecartControllableStepHeight;
+            } else {
+                maxUpStep = 0.0F;
+            }
+        } else {
+            maxUpStep = 0.0F;
+        }
+        // Purpur end
+
         if (this.onGround) {
             // CraftBukkit start - replace magic numbers with our variables
             this.setDeltaMovement(new Vec3(this.getDeltaMovement().x * this.derailedX, this.getDeltaMovement().y * this.derailedY, this.getDeltaMovement().z * this.derailedZ));
             // CraftBukkit end
         }
+        else if (level.purpurConfig.minecartControllable) setDeltaMovement(new Vec3(getDeltaMovement().x * derailedX, getDeltaMovement().y, getDeltaMovement().z * derailedZ)); // Purpur
 
         this.move(MoverType.SELF, this.getDeltaMovement());
         if (!this.onGround) {
diff --git a/src/main/java/net/minecraft/world/item/MinecartItem.java b/src/main/java/net/minecraft/world/item/MinecartItem.java
index 9ea3837acc315e5c57f28c63c356efd633f1e6cf..e8b76b67f972c2f44e7611434246a822d549a129 100644
--- a/src/main/java/net/minecraft/world/item/MinecartItem.java
+++ b/src/main/java/net/minecraft/world/item/MinecartItem.java
@@ -123,8 +123,9 @@ public class MinecartItem extends Item {
         BlockState iblockdata = world.getBlockState(blockposition);
 
         if (!iblockdata.is((Tag) BlockTags.RAILS)) {
-            return InteractionResult.FAIL;
-        } else {
+            if (!world.purpurConfig.minecartPlaceAnywhere) return InteractionResult.FAIL;
+            if (iblockdata.getMaterial().isSolid()) blockposition = blockposition.relative(context.getClickedFace());
+        } // else { // Purpur - place minecarts anywhere
             ItemStack itemstack = context.getItemInHand();
 
             if (!world.isClientSide) {
@@ -152,6 +153,6 @@ public class MinecartItem extends Item {
 
             itemstack.shrink(1);
             return InteractionResult.sidedSuccess(world.isClientSide);
-        }
+        // } // Purpur - place minecarts anywhere
     }
 }
diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
index 04d5ef90cd4171f9360017ac0c01ce48ae6ec983..7538262e14c86e4da9cd4cb887b76f649bfef2e6 100644
--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
@@ -70,7 +70,7 @@ import net.minecraft.world.phys.shapes.VoxelShape;
 public abstract class BlockBehaviour {
 
     protected static final Direction[] UPDATE_SHAPE_ORDER = new Direction[]{Direction.WEST, Direction.EAST, Direction.NORTH, Direction.SOUTH, Direction.DOWN, Direction.UP};
-    protected final Material material;
+    public final Material material; // Purpur - protected -> public
     protected final boolean hasCollision;
     protected final float explosionResistance;
     protected final boolean isRandomlyTicking;
diff --git a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
index 9ce64a3d7a00877688f1e45a3961159cb35c8d9f..51f8045f4f607db3cf63d30fa6783257466a6022 100644
--- a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
+++ b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
@@ -98,6 +98,68 @@ public class PurpurWorldConfig {
         armorstandStepHeight = (float) getDouble("gameplay-mechanics.armorstand.step-height", armorstandStepHeight);
     }
 
+    public double minecartMaxSpeed = 0.4D;
+    public boolean minecartPlaceAnywhere = false;
+    public boolean minecartControllable = false;
+    public float minecartControllableStepHeight = 1.0F;
+    public double minecartControllableHopBoost = 0.5D;
+    public boolean minecartControllableFallDamage = true;
+    public double minecartControllableBaseSpeed = 0.1D;
+    public Map<Block, Double> minecartControllableBlockSpeeds = new HashMap<>();
+    private void minecartSettings() {
+        if (PurpurConfig.version < 12) {
+            boolean oldBool = getBoolean("gameplay-mechanics.controllable-minecarts.place-anywhere", minecartPlaceAnywhere);
+            set("gameplay-mechanics.controllable-minecarts.place-anywhere", null);
+            set("gameplay-mechanics.minecart.place-anywhere", oldBool);
+            oldBool = getBoolean("gameplay-mechanics.controllable-minecarts.enabled", minecartControllable);
+            set("gameplay-mechanics.controllable-minecarts.enabled", null);
+            set("gameplay-mechanics.minecart.controllable.enabled", oldBool);
+            double oldDouble = getDouble("gameplay-mechanics.controllable-minecarts.step-height", minecartControllableStepHeight);
+            set("gameplay-mechanics.controllable-minecarts.step-height", null);
+            set("gameplay-mechanics.minecart.controllable.step-height", oldDouble);
+            oldDouble = getDouble("gameplay-mechanics.controllable-minecarts.hop-boost", minecartControllableHopBoost);
+            set("gameplay-mechanics.controllable-minecarts.hop-boost", null);
+            set("gameplay-mechanics.minecart.controllable.hop-boost", oldDouble);
+            oldBool = getBoolean("gameplay-mechanics.controllable-minecarts.fall-damage", minecartControllableFallDamage);
+            set("gameplay-mechanics.controllable-minecarts.fall-damage", null);
+            set("gameplay-mechanics.minecart.controllable.fall-damage", oldBool);
+            oldDouble = getDouble("gameplay-mechanics.controllable-minecarts.base-speed", minecartControllableBaseSpeed);
+            set("gameplay-mechanics.controllable-minecarts.base-speed", null);
+            set("gameplay-mechanics.minecart.controllable.base-speed", oldDouble);
+            ConfigurationSection section = getConfigurationSection("gameplay-mechanics.controllable-minecarts.block-speed");
+            if (section != null) {
+                for (String key : section.getKeys(false)) {
+                    if ("grass-block".equals(key)) key = "grass_block"; // oopsie
+                    oldDouble = section.getDouble(key, minecartControllableBaseSpeed);
+                    set("gameplay-mechanics.controllable-minecarts.block-speed." + key, null);
+                    set("gameplay-mechanics.minecart.controllable.block-speed." + key, oldDouble);
+                }
+                set("gameplay-mechanics.controllable-minecarts.block-speed", null);
+            }
+            set("gameplay-mechanics.controllable-minecarts", null);
+        }
+
+        minecartMaxSpeed = getDouble("gameplay-mechanics.minecart.max-speed", minecartMaxSpeed);
+        minecartPlaceAnywhere = getBoolean("gameplay-mechanics.minecart.place-anywhere", minecartPlaceAnywhere);
+        minecartControllable = getBoolean("gameplay-mechanics.minecart.controllable.enabled", minecartControllable);
+        minecartControllableStepHeight = (float) getDouble("gameplay-mechanics.minecart.controllable.step-height", minecartControllableStepHeight);
+        minecartControllableHopBoost = getDouble("gameplay-mechanics.minecart.controllable.hop-boost", minecartControllableHopBoost);
+        minecartControllableFallDamage = getBoolean("gameplay-mechanics.minecart.controllable.fall-damage", minecartControllableFallDamage);
+        minecartControllableBaseSpeed = getDouble("gameplay-mechanics.minecart.controllable.base-speed", minecartControllableBaseSpeed);
+        ConfigurationSection section = getConfigurationSection("gameplay-mechanics.minecart.controllable.block-speed");
+        if (section != null) {
+            for (String key : section.getKeys(false)) {
+                Block block = Registry.BLOCK.get(new ResourceLocation(key));
+                if (block != Blocks.AIR) {
+                    minecartControllableBlockSpeeds.put(block, section.getDouble(key, minecartControllableBaseSpeed));
+                }
+            }
+        } else {
+            set("gameplay-mechanics.minecart.controllable.block-speed.grass_block", 0.3D);
+            set("gameplay-mechanics.minecart.controllable.block-speed.stone", 0.5D);
+        }
+    }
+
     public boolean idleTimeoutKick = true;
     public boolean idleTimeoutTickNearbyEntities = true;
     public boolean idleTimeoutCountAsSleeping = false;
