import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; import org.bukkit.block.Block; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.util.EulerAngle; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.inventory.PlayerInventory; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.bukkit.entity.Entity; import org.bukkit.event.entity.EntityDamageEvent; public class DrillVehicle {     private final Player owner;     private final ArmorStand mainStand;     private final Set modelParts = new HashSet<>();     private double fuel = 100.0;     private boolean isDrilling = false;     private boolean isMoving = false;     private ArmorStand drillHead;     private boolean fuelMessageSent = false;     private float currentYaw = 0;     private static final double MOUNT_HEIGHT_OFFSET = -0.2;     public boolean isPartOfDrill(ArmorStand stand) {     return modelParts.contains(stand); }     private static final Set UNBREAKABLE_BLOCKS = new HashSet<>(Arrays.asList(         Material.BEDROCK,         Material.OBSIDIAN,         Material.BARRIER,         Material.END_PORTAL_FRAME,         Material.END_PORTAL,         Material.NETHER_PORTAL     ));     public DrillVehicle(Player owner) {         this.owner = owner;         Location spawnLoc = owner.getLocation();         spawnLoc.setY(spawnLoc.getY() + 0.1);         spawnLoc.setYaw(0);         currentYaw = 0;                 mainStand = spawnLoc.getWorld().spawn(spawnLoc, ArmorStand.class);         mainStand.setVisible(false);         mainStand.setGravity(false);         mainStand.setInvulnerable(true);         mainStand.setBasePlate(false);         mainStand.setArms(false);         mainStand.setSmall(false);         mainStand.setMarker(true);         mainStand.setCanPickupItems(false);         mainStand.setRemoveWhenFarAway(false);                 createDrillModel();         startHandlers();     }     private void createDrillModel() {         Location loc = mainStand.getLocation();                 // Base platform (2x3 flat platform)         double spacing = 0.4;         for (int x = -1; x <= 1; x++) {             for (int z = -1; z <= 1; z++) {                 createBodyPart(loc, x * spacing, 0, z * spacing, Material.IRON_BLOCK);             }         }         // Walls (1 block high, on the edges) - now 0.75x height         double wallHeight = 1.2;                 // Left and right walls with glass         for (double y = 0.4; y <= wallHeight; y += 0.4) {             // Left wall             createBodyPart(loc, -spacing, y, -spacing, Material.BLACK_CONCRETE);             createBodyPart(loc, -spacing, y, 0, Material.GLASS);             createBodyPart(loc, -spacing, y, spacing, Material.BLACK_CONCRETE);                         // Right wall             createBodyPart(loc, spacing, y, -spacing, Material.BLACK_CONCRETE);             createBodyPart(loc, spacing, y, 0, Material.GLASS);             createBodyPart(loc, spacing, y, spacing, Material.BLACK_CONCRETE);                         // Back wall (between concrete)             createBodyPart(loc, 0, y, -spacing, Material.GLASS);         }         // 3x3 Gold connection platform (without glass on top)         for (double y = 0.4; y <= 0.8; y += 0.4) {             for (int x = -1; x <= 1; x++) {                 createBodyPart(loc, x * 0.4, y, 0.8, Material.GOLD_BLOCK);             }         }         // Front drill assembly         createBodyPart(loc, -0.2, 0.5, 1.2, Material.DIAMOND_BLOCK);         createBodyPart(loc, 0.2, 0.5, 1.2, Material.DIAMOND_BLOCK);         createBodyPart(loc, 0, 0.9, 1.2, Material.DIAMOND_BLOCK);                 drillHead = createModelPart(loc.clone().add(0, 0.7, 1.6));         drillHead.getEquipment().setHelmet(new ItemStack(Material.DIAMOND_BLOCK));         drillHead.setSmall(true);         modelParts.add(drillHead);                 // Make all parts non-pickupable         for (ArmorStand part : modelParts) {             part.setCanPickupItems(false);         }     }     // This method handles the vehicle controls for WASD movement     public void handleVehicleControls(float forward, float sideways, boolean jump) {         if (owner.getVehicle() != mainStand || fuel <= 0) {             if (!fuelMessageSent && fuel <= 0) {                 owner.sendMessage("§cOUT OF FUEL");                 fuelMessageSent = true;             }             return;         }         // Make sure player is still mounted         if (owner.getVehicle() != mainStand) {             mount(); // Try to remount if somehow dismounted             return;         }         double baseSpeed = DrillPlugin.getInstance().getConfig().getDouble("movement-speed", 0.3);                 if (forward != 0 || sideways != 0) {             // Calculate movement vectors             double yaw = Math.toRadians(currentYaw);             Vector direction = new Vector(-Math.sin(yaw), 0, Math.cos(yaw));             Vector right = new Vector(-direction.getZ(), 0, direction.getX());                         // Combine movements             Vector movement = direction.multiply(forward).add(right.multiply(-sideways));             movement.normalize().multiply(baseSpeed);                         // Apply movement             Location newLoc = mainStand.getLocation().add(movement);             if (newLoc.getBlock().getType() == Material.AIR) {                 newLoc.setYaw(currentYaw);                 mainStand.teleport(newLoc);                                 // Keep player in sync                 Location playerLoc = newLoc.clone();                 playerLoc.setY(newLoc.getY() + MOUNT_HEIGHT_OFFSET);                 playerLoc.setYaw(currentYaw);                 owner.teleport(playerLoc);                                 updateModelParts();                 consumeFuel(0.1);             }         }                     if (jump) {             currentYaw = (currentYaw + 180) % 360;             Location loc = mainStand.getLocation();             loc.setYaw(currentYaw);             mainStand.teleport(loc);                         Location playerLoc = owner.getLocation();             playerLoc.setYaw(currentYaw);             owner.teleport(playerLoc);                         updateModelParts();         }     }     private void startHandlers() {         new BukkitRunnable() {             u/Override             public void run() {                 if (!mainStand.isValid() || !owner.isOnline()) {                     remove();                     cancel();                     return;                 }                                 if (isDrilling) {                     handleDrilling();                 }             }         }.runTaskTimer(DrillPlugin.getInstance(), 1L, 1L);     }     public void toggleMovement() {         isMoving = !isMoving;         String message = isMoving ?             DrillPlugin.getInstance().getConfig().getString("messages.movement-activated", "§aDrill movement activated!") :             DrillPlugin.getInstance().getConfig().getString("messages.movement-deactivated", "§cDrill movement deactivated!");         owner.sendMessage(message);     }   private void handleMovement() {         if (fuel <= 0) {             if (!fuelMessageSent) {                 owner.sendMessage("§cOUT OF FUEL");                 fuelMessageSent = true;             }             isMoving = false;             return;         }         Vector direction = owner.getLocation().getDirection().setY(0).normalize();         double moveSpeed = DrillPlugin.getInstance().getConfig().getDouble("movement-speed", 0.3);         Location currentLoc = mainStand.getLocation();         Location newLoc = currentLoc.clone().add(direction.multiply(moveSpeed));                 if (newLoc.getBlock().getType().equals(Material.AIR)) {             // Keep current yaw when moving             newLoc.setYaw(currentYaw);             mainStand.teleport(newLoc);             updateModelParts();             consumeFuel(0.1);         }     }     private void handleDrilling() {         if (fuel <= 0) {             isDrilling = false;             return;         }                 if (drillHead != null) {             EulerAngle rotation = drillHead.getHeadPose();             drillHead.setHeadPose(rotation.add(0, 0.5, 0));         }                 Location loc = mainStand.getLocation();         Vector direction = getForwardVector();                 for (int i = 1; i <= 3; i++) {             Location blockLoc = loc.clone().add(direction.clone().multiply(i + 1));             for (int y = -1; y <= 1; y++) {                 for (int x = -1; x <= 1; x++) {                     Vector right = new Vector(direction.getZ(), 0, -direction.getX());                     Location miningLoc = blockLoc.clone().add(right.multiply(x)).add(0, y, 0);                     Block block = miningLoc.getBlock();                     if (block.getType() != Material.AIR && !UNBREAKABLE_BLOCKS.contains(block.getType())) {                         block.breakNaturally();                     }                 }             }         }                 consumeFuel(0.5);     }             public void mount() {     if (owner != null && mainStand != null) {         mainStand.addPassenger(owner);                 new BukkitRunnable() {             u/Override             public void run() {                 Location mountLoc = mainStand.getLocation().clone();                 mountLoc.setY(mainStand.getLocation().getY() + MOUNT_HEIGHT_OFFSET);                 mountLoc.setYaw(currentYaw);                 mountLoc.setPitch(0);                 owner.teleport(mountLoc);                                 new BukkitRunnable() {                     u/Override                     public void run() {                         mainStand.addPassenger(owner);                         Location playerLoc = owner.getLocation();                         playerLoc.setYaw(currentYaw);                         owner.teleport(playerLoc);                     }                 }.runTaskLater(DrillPlugin.getInstance(), 2L);             }         }.runTaskLater(DrillPlugin.getInstance(), 1L);     } else {         if (owner != null) {             owner.sendMessage("Failed to mount: mainStand is null");         }     }         }         private Vector getForwardVector() {         double rad = Math.toRadians(currentYaw);         return new Vector(-Math.sin(rad), 0, Math.cos(rad)).normalize();     }     private void consumeFuel(double amount) {         FileConfiguration config = DrillPlugin.getInstance().getConfig();         double maxFuelCapacity = config.getDouble("fuel.max-fuel-capacity", 500);         if (fuel > 0) {             fuel -= amount;             if (fuel < 0) fuel = 0;             fuelMessageSent = false;             return;         }         PlayerInventory inventory = owner.getInventory();         double fuelPerCoal = config.getDouble("fuel.coal-fuel-amount", 50);         ItemStack coal = null;         for (ItemStack item : inventory.getContents()) {             if (item != null && item.getType() == Material.COAL) {                 coal = item;                 break;             }         }         if (coal != null) {             double addedFuel = Math.min(fuelPerCoal, maxFuelCapacity);             fuel += addedFuel;             coal.setAmount(coal.getAmount() - 1);                         String message = config.getString("messages.fuel-added", "§aAdded fuel! Current fuel level: §6%fuel%")                 .replace("%fuel%", String.format("%.1f", fuel));             owner.sendMessage(message);             fuelMessageSent = false;         }     }     public void addFuel(ItemStack item) {         if (item == null || item.getType() != Material.COAL) {             return;         }         FileConfiguration config = DrillPlugin.getInstance().getConfig();         double maxFuelCapacity = config.getDouble("fuel.max-fuel-capacity", 500);         double fuelPerCoal = config.getDouble("fuel.coal-fuel-amount", 50);         double addedFuel = Math.min(fuelPerCoal, maxFuelCapacity - fuel);                 if (addedFuel > 0) {             fuel += addedFuel;             item.setAmount(item.getAmount() - 1);                         String message = config.getString("messages.fuel-added", "§aAdded fuel! Current fuel level: §6%fuel%")                 .replace("%fuel%", String.format("%.1f", fuel));             owner.sendMessage(message);             fuelMessageSent = false;         }     }     public void toggleDrilling() {         isDrilling = !isDrilling;         String message = isDrilling ?             "§aDrill activated!" :             "§cDrill deactivated!";         owner.sendMessage(message);     }     public void dismount() {         if (owner.getVehicle() == mainStand) {             Location dismountLoc = mainStand.getLocation().clone().add(0, 1.5, 0);             owner.leaveVehicle();             owner.teleport(dismountLoc);         }     }     public void rotate() {         if (owner.getVehicle() == mainStand) {             currentYaw = (currentYaw + 90) % 360;             Location loc = mainStand.getLocation();             loc.setYaw(currentYaw);             mainStand.teleport(loc);                         Location playerLoc = owner.getLocation();             playerLoc.setYaw(currentYaw);             owner.teleport(playerLoc);                         updateModelParts();         }     }     private void updateModelParts() {         Location baseLoc = mainStand.getLocation();         for (ArmorStand part : modelParts) {             Location partLoc = baseLoc.clone();             Vector offset = part.getLocation().subtract(baseLoc).toVector();             offset = rotateVector(offset, currentYaw);             partLoc.add(offset);             partLoc.setYaw(currentYaw);             part.teleport(partLoc);         }     }     private Vector rotateVector(Vector vector, float yaw) {         double rad = Math.toRadians(yaw);         double x = vector.getX() * Math.cos(rad) - vector.getZ() * Math.sin(rad);         double z = vector.getX() * Math.sin(rad) + vector.getZ() * Math.cos(rad);         return new Vector(x, vector.getY(), z);     }     public void remove() {         mainStand.remove();         for (ArmorStand part : modelParts) {             part.remove();         }         modelParts.clear();     }     public ArmorStand getMainStand() {         return mainStand;     }     public Player getOwner() {         return owner;     }     private ArmorStand createModelPart(Location loc) {         ArmorStand stand = loc.getWorld().spawn(loc, ArmorStand.class);         stand.setVisible(false);         stand.setGravity(false);         stand.setInvulnerable(true);         stand.setSmall(true);         modelParts.add(stand);         return stand;     }     private void createBodyPart(Location loc, double x, double y, double z, Material material) {         ArmorStand part = createModelPart(loc.clone().add(x, y, z));         part.getEquipment().setHelmet(new ItemStack(material));     } }