de59059

AKL City ยท akl-city.git

Branches > main > de59059 > files

Clean up Minecarts.java

Grahp, 2026-05-24

Commit: de59059babb0f64a3d844549515d45587756fd72
Parent: 09ec71f

1 changed file:

src/main/java/dev/grahp/aklcity/Minecarts.java modified (view file)

@@ -2,96 +2,52 @@  import static dev.grahp.aklcity.PlayerUtils.isHoldingFist; -import org.bukkit.event.Listener;-import org.bukkit.event.EventHandler;-import org.bukkit.Material;-import org.bukkit.Location;+import org.bukkit.*;+import org.bukkit.event.*;+import org.bukkit.event.vehicle.*;+import org.bukkit.event.player.PlayerInteractEvent;+import org.bukkit.event.block.Action; import org.bukkit.block.Block;-import org.bukkit.entity.Minecart;-import org.bukkit.entity.Vehicle;-import org.bukkit.entity.Player;-import org.bukkit.event.vehicle.VehicleMoveEvent;-import org.bukkit.util.Vector;-import org.bukkit.Bukkit; import org.bukkit.block.data.Powerable;-import java.util.HashSet;-import java.util.Set;-import java.util.UUID;-import java.util.Map;-import java.util.HashMap;+import org.bukkit.entity.*;+import org.bukkit.util.Vector; import org.bukkit.scheduler.BukkitTask; +import java.util.*;+ import net.kyori.adventure.text.Component; -import org.bukkit.World;--import org.bukkit.event.vehicle.VehicleExitEvent;-import org.bukkit.event.vehicle.VehicleEnterEvent;--import org.bukkit.event.player.PlayerInteractEvent;-import org.bukkit.event.block.Action;  // TODO: // - Stops mobs from pathfinding onto rails? Or... Just kill them when they do smiemsie, keep momentum+// Somehow tell whether the player is going the wrong way?  public class Minecarts implements Listener {+  private static final Material stopperRailMaterial = Material.DETECTOR_RAIL;+  private static final long timeToWaitForMinecartsToBeKilledAfterBeingDismounted = 5L;+  private static final double minecartVelocityThreshold = 0.001;+  private static final long ticksToStopAtStops = 20L;+  private static final long extraTicksToDismountAfterDeparting = 10L; -  // Returns a safe place to dismount the player if there is one, or just the normal position a player would dismount a minecart from.-  private Location findSafeDismount(Minecart cart) {-    Location base = cart.getLocation();-    World world = base.getWorld();--    // for (int dx = -2; dx <= 2; dx++) {-    //   for (int dz = -2; dz <= 2; dz++) {-    //-    //     Location feet = base.clone().add(dx, 0, dz);-    //-    //     Block ground = feet.clone().subtract(0, 1, 0).getBlock();-    //     Block feetBlock = feet.getBlock();-    //     Block headBlock = feet.clone().add(0, 1, 0).getBlock();-    //-    //     boolean solidGround = ground.getType().isSolid();-    //     boolean clear = feetBlock.isPassable() && headBlock.isPassable();-    //     boolean notRail =-    // !feetBlock.getType().toString().endsWith("RAIL") &&-    // !ground.getType().toString().endsWith("RAIL");-    //-    //     if (solidGround && clear && notRail) {-    //       return feet.add(0.5, 0, 0.5);-    //     }-    //   }-    // }-    //-    // Bukkit.broadcastMessage("choosing default");-    return base;+  /// Returns whether a player is in a Minecart+  private boolean isPlayerInMinecart(Minecart cart) {+    return !cart.getPassengers().isEmpty() && cart.getPassengers().get(0) instanceof Player;   } -  private Player playerInMinecart(Minecart cart) {-    if (!cart.getPassengers().isEmpty() && cart.getPassengers().get(0) instanceof Player p) {-      return p;-    }-    return null;-  }-+  // TODO should find a safe point to dismount the player and tp them there   private void dismount(Minecart cart) {     cart.eject();-    // var p = playerInMinecart(cart);-    // if (p != null) {-    //   p.teleport(findSafeDismount(cart));-    // }   }    private void killMinecart(Minecart cart) {     dismount(cart);-    Timers.schedule(5L, () -> {+    Timers.schedule(timeToWaitForMinecartsToBeKilledAfterBeingDismounted, () -> {       if (!cart.isDead()) {         cart.remove();       }     });   } -  private static final Material stopperRailMaterial = Material.DETECTOR_RAIL;-   /// Returns whether a Minecart is on top of a rail that it should slow down and allow the player to exit for   private boolean isOnStopperRail(Minecart cart) {     return cart.getLocation().getBlock().getType() == stopperRailMaterial;@@ -99,97 +55,99 @@    /// Returns whether a Minecart is stopped, or moving so slow that it's basically not moving at all   private boolean isCartStopped(Minecart cart) {-        return cart.getVelocity().lengthSquared() < 0.001;+    return cart.getVelocity().lengthSquared() < minecartVelocityThreshold;   }    @EventHandler   public void onEnterMinecart(VehicleEnterEvent e) {     if (e.getVehicle() instanceof Minecart && e.getEntered() instanceof Player p) {+      // Needs to be delayed a tick to override the normal message       Timers.runNextTick(() -> {-      p.sendActionBar(-          Component.text("Press ")-          .append(Component.keybind("key.sneak"))-          .append(Component.text(" to exit at the next stop"))-          );+        p.sendActionBar(+            Component.text("Press ")+            .append(Component.keybind("key.sneak"))+            .append(Component.text(" to exit at the next stop"))+            );       });     }   } -  private static Map<UUID, Location> stopLocations = new HashMap();-   @EventHandler-  public void onExitMinecart(VehicleExitEvent e) {+  public void onExitingMinecart(VehicleExitEvent e) {     if (e.getVehicle() instanceof Minecart cart && cart.getPassengers().get(0) instanceof Player p) {-      if (isOnStopperRail(cart) || isCartStopped(cart)) {+      if (isCartStopped(cart)) {         killMinecart(cart);-      } else if (stopLocations.containsKey(p.getUniqueId())) {-        // TODO looks similar to killMinecart...-        cart.eject();-        cart.remove();-        p.teleport(stopLocations.get(p.getUniqueId()));+        if (stopLocations.containsKey(cart)) {+          p.teleport(stopLocations.get(cart));+        }       } else {         e.setCancelled(true);         p.sendActionBar(Component.text("You may only exit at a stop!"));       }-      stopLocations.remove(p.getUniqueId());+      stopLocations.remove(cart);     }   } -  // Called once when a Minecart enters a stop-  public BukkitTask onEnterStop(Minecart cart) {-    var p = playerInMinecart(cart);-    if (p == null) {-      killMinecart(cart);-      return null;-    } else {-      var velocity = cart.getVelocity().clone();-      cart.setVelocity(new Vector(0, 0, 0));-      var puuid = p.getUniqueId();-            stopLocations.put(puuid, cart.getLocation());+  private Map<Minecart, Location> stopLocations = new HashMap();+  private Map<Minecart, Vector> previousSpeeds = new HashMap();+  private Set<Minecart> cartsAtStops = new HashSet(); -      var task = Timers.schedule(20L, () -> {-        if (!cart.isDead() && cart.isValid()) {-          cart.setVelocity(velocity.normalize().multiply(0.4));-                  }+  private enum StopType {+    NORMAL, // Waited for the minecart to stop at its stop and then continue on+    SKIPPED // Manually pressed W to move on to the next stop+  }++  /// Departs the Minecart from the stop it's stopped at.+  /// When called after a cart has already departed, does nothing.+  /// After a delay, a stopped minecart will automatically go forward,+  /// but the player may choose to do this manually. When skipping a stop,+  /// extra leeway is not given to allow the player to exit the stop after the+  /// cart has resumed motion.+  private void departFromStop(Minecart cart, StopType type) {+    if (!cart.isDead() && cart.isValid() && cartsAtStops.contains(cart) && previousSpeeds.containsKey(cart)) {+      cart.setVelocity(previousSpeeds.get(cart).normalize().multiply(0.4));+    }+    switch (type) {+      case NORMAL -> Timers.schedule(extraTicksToDismountAfterDeparting, () -> {+        stopLocations.remove(cart);       });-      // TODO also need to clear the stop when the player chooses to go forwards on their own-      Timers.schedule(30L, () -> {-        stopLocations.remove(puuid); // Could clear the wrong thing?-              });-      return task;+      case SKIPPED -> stopLocations.remove(cart);     }+    previousSpeeds.remove(cart);   } -  private static Set<UUID> cartsAtStops = new HashSet();-  private static Map<UUID, BukkitTask> tasks = new HashMap();+  public void onEnterStop(Minecart cart) {+    if (!isPlayerInMinecart(cart)) {+      killMinecart(cart);+    } else {+      cartsAtStops.add(cart);+      previousSpeeds.put(cart, cart.getVelocity().clone());+      stopLocations.put(cart, cart.getLocation());+      cart.setVelocity(new Vector(0, 0, 0));++      Timers.schedule(ticksToStopAtStops, () -> {+        departFromStop(cart, StopType.NORMAL);+      });+    }+  }    // TODO Being block-based has the fundamental problem that travelling too fast can cause you to skip over a block entirely   @EventHandler   public void onMove(VehicleMoveEvent e) {-    if (e.getVehicle() instanceof Minecart cart && cart.getPassengers().get(0) instanceof Player p) {-      var uuid = cart.getUniqueId();-      if (isOnStopperRail(cart)) {-        if (cartsAtStops.contains(uuid)) {-          // If you're moving forward, resume velocity.-          // TODO Extract this and the task above yeag-          if (cart.getVelocity().lengthSquared() > 0) {-            tasks.get(uuid).cancel();-            // Only runninng after the timer has been set...-            if (!cart.isDead() && cart.isValid()) {-              cart.setVelocity(cart.getVelocity().clone().normalize().multiply(0.4));-              stopLocations.remove(p.getUniqueId());-            }-          }-        } else {-          var task = onEnterStop(cart);-          if (task != null) {-            tasks.put(uuid, task);-          }-          cartsAtStops.add(uuid);-        }-      } else {-        cartsAtStops.remove(uuid);-        tasks.remove(uuid);+    if (e.getVehicle() instanceof Minecart cart && isPlayerInMinecart(cart)) {+      var movingAtStop = isOnStopperRail(cart);+      var alreadyAtStop = cartsAtStops.contains(cart);++      if (movingAtStop && !alreadyAtStop) {+        onEnterStop(cart);+      }++      if (movingAtStop && alreadyAtStop) {+        departFromStop(cart, StopType.SKIPPED);+      }++      if (!movingAtStop) {+        cartsAtStops.remove(cart);       }     }   }