Skip to content
Permalink
Browse files

Implements Elevator and Teleporter mechanic while riding a horse. (#220)

* Fixes Elevator teleport when player is mounted on horse / is inside vehicle.

* Moves teleportVehicle to teleportPlayerVehicle to use it in Teleporter as well.

* Implements Elevator functionality while player is in vehicle with smooth-movement: true.

* Fixes Elevator player moving to side when ejecting out of vehicle.

* Implements Elevator smooth-movement through solid blocks between start and destination.

* Refactors Elevator.java.

* Further refactoring of Elevator.java.

* Refactors Elevator.java.

* Removes unnecessary code from Elevator.java and adds some comments.

* Re-adds previously removed code because edge-case tests showed it was actually necessary.

* Adds Javadocs for LocationUtil public methods.

* Increases addVehiclePassengerDelayed runnableDelayInTicks from 4 to 6 ticks.

* Reverts import changes.
  • Loading branch information
Xericore authored and me4502 committed Jan 28, 2020
1 parent 03ff484 commit 3ba53f06aeb71190143a1f3caf83435ab1a89cb5
@@ -34,13 +34,13 @@
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.Tag;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.type.Switch;
import org.bukkit.entity.Player;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.Action;
@@ -53,6 +53,7 @@
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
@@ -67,11 +68,15 @@
public class Elevator extends AbstractCraftBookMechanic {

private HashSet<UUID> flyingPlayers;
private HashMap<UUID, Vehicle> playerVehicles;

@Override
public boolean enable() {
if(elevatorSlowMove)
if(elevatorSlowMove) {
flyingPlayers = new HashSet<>();
playerVehicles = new HashMap<>();
}

return true;
}

@@ -367,87 +372,152 @@ public void teleportPlayer(final CraftBookPlayer player, final Block floor, fina

final Location lastLocation = CraftBookBukkitUtil.toLocation(player.getLocation());

if (player.isInsideVehicle()) {
Player bukkitPlayer = ((BukkitCraftBookPlayer)player).getPlayer();
playerVehicles.put(player.getUniqueId(), (Vehicle)bukkitPlayer.getVehicle());

LocationUtil.ejectAndTeleportPlayerVehicle(player, newLocation);

// Ejecting the player out of the vehicle will move
// the player to the side, so we have to correct this.
bukkitPlayer.teleport(lastLocation);
}

new BukkitRunnable(){
@Override
public void run () {
OfflinePlayer op = ((BukkitCraftBookPlayer)player).getPlayer();
if(!op.isOnline()) {

OfflinePlayer offlinePlayer = ((BukkitCraftBookPlayer)player).getPlayer();
if(!offlinePlayer.isOnline()) {
cancel();
return;
}
Player p = op.getPlayer();
Player p = offlinePlayer.getPlayer();
if(!flyingPlayers.contains(p.getUniqueId()) && !p.getAllowFlight())
flyingPlayers.add(p.getUniqueId());
p.setAllowFlight(true);
p.setFlying(true);
p.setFallDistance(0f);
p.setNoDamageTicks(2);
double speed = elevatorMoveSpeed;

enableFlightMode(p);

newLocation.setPitch(p.getLocation().getPitch());
newLocation.setYaw(p.getLocation().getYaw());

if(Math.abs(newLocation.getY() - p.getLocation().getY()) < 0.7) {
p.teleport(newLocation);
teleportFinish(player, destination, shift);
if(flyingPlayers.contains(p.getUniqueId())) {
p.setFlying(false);
p.setAllowFlight(p.getGameMode() == GameMode.CREATIVE);
flyingPlayers.remove(p.getUniqueId());
}
cancel();
boolean isPlayerAlmostAtDestination = Math.abs(floor.getY() - p.getLocation().getY()) < 0.7;

if(isPlayerAlmostAtDestination) {
finishElevatingPlayer(p);
return;
}

if(lastLocation.getBlockX() != p.getLocation().getBlockX() || lastLocation.getBlockZ() != p.getLocation().getBlockZ()) {
boolean didPlayerLeaveElevator =
lastLocation.getBlockX() != p.getLocation().getBlockX() ||
lastLocation.getBlockZ() != p.getLocation().getBlockZ();

if(didPlayerLeaveElevator) {
player.print("mech.lift.leave");
if(flyingPlayers.contains(p.getUniqueId())) {
p.setFlying(false);
p.setAllowFlight(p.getGameMode() == GameMode.CREATIVE);
flyingPlayers.remove(p.getUniqueId());
}
disableFlightMode(p);
playerVehicles.remove(p.getUniqueId());
cancel();
return;
}

if(newLocation.getY() > p.getLocation().getY()) {
p.setVelocity(new Vector(0, speed,0));
if(p.getLocation().add(0, 2, 0).getBlock().getType().isSolid())
p.teleport(p.getLocation().add(0, speed, 0));
} else if (newLocation.getY() < p.getLocation().getY()) {
p.setVelocity(new Vector(0, -speed,0));
if(p.getLocation().add(0, -1, 0).getBlock().getType().isSolid())
p.teleport(p.getLocation().add(0, -speed, 0));
} else {
teleportFinish(player, destination, shift);
if(flyingPlayers.contains(p.getUniqueId())) {
p.setFlying(false);
p.setAllowFlight(p.getGameMode() == GameMode.CREATIVE);
flyingPlayers.remove(p.getUniqueId());
}
cancel();
return;
Direction playerVerticalMovement = getVerticalDirection(p.getLocation(), newLocation);

switch (playerVerticalMovement) {
case UP:
// Teleporting the player up inside solid blocks will not execute
// the teleport but rather cause the player to "swim" in mid air.
// See https://dev.enginehub.org/youtrack/issue/CRAFTBOOK-3464
// Thus we'll simply teleport the player to the ceiling in that case.
if (isSolidBlockOccludingMovement(p, playerVerticalMovement)) {
finishElevatingPlayer(p);
return;
} else {
p.setVelocity(new Vector(0, elevatorMoveSpeed, 0));
}
break;
case DOWN:
// Contrary to moving the player up,
// moving down into solid blocks works just fine.
p.setVelocity(new Vector(0, -elevatorMoveSpeed, 0));
if (isSolidBlockOccludingMovement(p, playerVerticalMovement))
p.teleport(p.getLocation().add(0, -elevatorMoveSpeed, 0));
break;
default:
// Player is not moving
finishElevatingPlayer(p);
return;
}

lastLocation.setY(p.getLocation().getY());
}

private void finishElevatingPlayer(Player p) {
p.teleport(newLocation);
teleportFinish(player, destination, shift);
disableFlightMode(p);
setPassengerIfPlayerWasInVehicle(player);
cancel();
}

}.runTaskTimer(CraftBookPlugin.inst(), 1, 1);
} else {
// Teleport!
if (player.isInsideVehicle()) {
Vehicle teleportedVehicle = LocationUtil.ejectAndTeleportPlayerVehicle(player, newLocation);

newLocation.setX(((BukkitCraftBookPlayer)player).getPlayer().getVehicle().getLocation().getX());
newLocation.setY(floor.getY() + 2);
newLocation.setZ(((BukkitCraftBookPlayer)player).getPlayer().getVehicle().getLocation().getZ());
newLocation.setYaw(((BukkitCraftBookPlayer)player).getPlayer().getVehicle().getLocation().getYaw());
newLocation.setPitch(((BukkitCraftBookPlayer)player).getPlayer().getVehicle().getLocation().getPitch());
((BukkitCraftBookPlayer)player).getPlayer().getVehicle().teleport(newLocation);
player.setPosition(BukkitAdapter.adapt(newLocation).toVector(), newLocation.getPitch(), newLocation.getYaw());

LocationUtil.addVehiclePassengerDelayed(teleportedVehicle, player);
} else {
player.setPosition(BukkitAdapter.adapt(newLocation).toVector(), newLocation.getPitch(), newLocation.getYaw());
}
player.setPosition(BukkitAdapter.adapt(newLocation).toVector(), newLocation.getPitch(), newLocation.getYaw());

teleportFinish(player, destination, shift);
}
}

private void enableFlightMode(Player p) {
p.setAllowFlight(true);
p.setFlying(true);
p.setFallDistance(0f);
p.setNoDamageTicks(2);
}

private void disableFlightMode(Player p) {
if (!flyingPlayers.contains(p.getUniqueId())) {
return;
}
p.setFlying(false);
p.setAllowFlight(p.getGameMode() == GameMode.CREATIVE);
flyingPlayers.remove(p.getUniqueId());
}

private Direction getVerticalDirection(Location from, Location to)
{
if(from.getY() < to.getY())
return Direction.UP;
else if (from.getY() > to.getY())
return Direction.DOWN;
else
return Direction.NONE;
}

private boolean isSolidBlockOccludingMovement(Player p, Direction direction) {
int verticalDistance = direction == Direction.UP ? 2 : -1;
return p.getLocation().clone().add(0, verticalDistance, 0).getBlock().getType().isSolid();
}

private void setPassengerIfPlayerWasInVehicle(CraftBookPlayer player) {

boolean wasPlayerInVehicle = playerVehicles.containsKey(player.getUniqueId());

if(wasPlayerInVehicle) {
Vehicle vehicle = playerVehicles.get(player.getUniqueId());
LocationUtil.addVehiclePassengerDelayed(vehicle, player);
playerVehicles.remove(player.getUniqueId());
}
}

public static void teleportFinish(CraftBookPlayer player, Block destination, BlockFace shift) {
// Now, we want to read the sign so we can tell the player
// his or her floor, but as that may not be avilable, we can
@@ -3,22 +3,22 @@
import com.sk89q.craftbook.AbstractCraftBookMechanic;
import com.sk89q.craftbook.ChangedSign;
import com.sk89q.craftbook.CraftBookPlayer;
import com.sk89q.craftbook.bukkit.BukkitCraftBookPlayer;
import com.sk89q.craftbook.bukkit.CraftBookPlugin;
import com.sk89q.craftbook.bukkit.util.CraftBookBukkitUtil;
import com.sk89q.craftbook.util.EventUtil;
import com.sk89q.craftbook.util.LocationUtil;
import com.sk89q.craftbook.util.ParsingUtil;
import com.sk89q.craftbook.util.ProtectionUtil;
import com.sk89q.craftbook.util.RegexUtil;
import com.sk89q.craftbook.util.SignUtil;
import com.sk89q.craftbook.util.events.SignClickEvent;
import com.sk89q.util.yaml.YAMLProcessor;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.util.Location;
import org.bukkit.Tag;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.Directional;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.Action;
@@ -204,20 +204,24 @@ private void makeItSo(CraftBookPlayer player, Block trigger) {
subspaceRift = subspaceRift.setX(floor.getX() + 0.5);
subspaceRift = subspaceRift.setY(floor.getY() + 1.0);
subspaceRift = subspaceRift.setZ(floor.getZ() + 0.5);
if (player.isInsideVehicle()) {
subspaceRift = BukkitAdapter.adapt(((BukkitCraftBookPlayer)player).getPlayer().getVehicle().getLocation());
subspaceRift = subspaceRift.setX(floor.getX() + 0.5);
subspaceRift = subspaceRift.setY(floor.getY() + 2.0);
subspaceRift = subspaceRift.setZ(floor.getZ() + 0.5);
((BukkitCraftBookPlayer)player).getPlayer().getVehicle().teleport(CraftBookBukkitUtil.toLocation(subspaceRift));
}
if (maxRange > 0)

if (maxRange > 0) {
if (subspaceRift.toVector().distanceSq(player.getLocation().toVector()) > maxRange * maxRange) {
player.print("mech.teleport.range");
return;
}
}

player.teleport(subspaceRift);
if (player.isInsideVehicle()) {
org.bukkit.Location newLocation = CraftBookBukkitUtil.toLocation(subspaceRift);
Vehicle teleportedVehicle = LocationUtil.ejectAndTeleportPlayerVehicle(player, newLocation);

player.teleport(subspaceRift);

LocationUtil.addVehiclePassengerDelayed(teleportedVehicle, player);
} else {
player.teleport(subspaceRift);
}

player.print("mech.teleport.alert");
}
@@ -1,6 +1,8 @@
package com.sk89q.craftbook.util;

import com.sk89q.craftbook.ChangedSign;
import com.sk89q.craftbook.CraftBookPlayer;
import com.sk89q.craftbook.bukkit.BukkitCraftBookPlayer;
import com.sk89q.craftbook.bukkit.CraftBookPlugin;
import com.sk89q.craftbook.bukkit.util.CraftBookBukkitUtil;
import com.sk89q.worldedit.math.Vector3;
@@ -12,6 +14,8 @@
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Vehicle;
import org.bukkit.scheduler.BukkitRunnable;

import java.util.HashSet;

@@ -308,4 +312,65 @@ public static boolean isBorderChunk(Chunk chunk) {

return new BlockFace[] {BlockFace.UP, BlockFace.DOWN, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST, BlockFace.NORTH_EAST, BlockFace.NORTH_WEST, BlockFace.SOUTH_EAST, BlockFace.SOUTH_WEST};
}


/**
* Teleports the vehicle the player is in to the given destination.
* Player is ejected out of the vehicle prior to teleportation,
* otherwise it doesn't work.
* @param player The player that will be ejected and whose vehicle will be teleported.
* @param newLocation The location the vehicle will be teleported to.
* @return The {@link Vehicle} the player was in or null if player was not in vehicle.
*/
public static Vehicle ejectAndTeleportPlayerVehicle(CraftBookPlayer player, Location newLocation) {

Player bukkitPlayer = ((BukkitCraftBookPlayer)player).getPlayer();

if(bukkitPlayer == null || !bukkitPlayer.isInsideVehicle())
return null;

Vehicle vehicle = (Vehicle)bukkitPlayer.getVehicle();

if(vehicle == null)
return null;

newLocation.setYaw(vehicle.getLocation().getYaw());
newLocation.setPitch(vehicle.getLocation().getPitch());

// Vehicle must eject the passenger first,
// otherwise vehicle.teleport() will not have any effect.
vehicle.eject();
vehicle.teleport(newLocation);
return vehicle;
}


/**
* Adds the player to the vehicle. Execution is delayed
* by six ticks through a {@link BukkitRunnable} because
* it doesn't work otherwise.
*
* @param vehicle The vehicle that will set the player as a passenger.
* @param player The player that will be put inside the provided vehicle.
*/
public static void addVehiclePassengerDelayed(Vehicle vehicle, CraftBookPlayer player) {

Player bukkitPlayer = ((BukkitCraftBookPlayer)player).getPlayer();

if(bukkitPlayer == null || vehicle == null)
return;

// The runnableDelayInTicks = 6 was the lowest number that
// worked reliably across several tests.
long runnableDelayInTicks = 6;

// vehicle.teleport() seems to have a delay. Calling vehicle.setPassenger()
// without the delayed runnable will not set the passenger.
new BukkitRunnable(){
@Override
public void run () {
vehicle.addPassenger(bukkitPlayer);
}
}.runTaskLater(CraftBookPlugin.inst(), runnableDelayInTicks);
}
}

0 comments on commit 3ba53f0

Please sign in to comment.
You can’t perform that action at this time.