diff --git a/docs/images/ArmPoses.jpg b/docs/images/ArmPoses.jpg new file mode 100644 index 0000000..5fdbe41 Binary files /dev/null and b/docs/images/ArmPoses.jpg differ diff --git a/pom.xml b/pom.xml index 3b7a6cb..f7491b3 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ com.rsscripting rs-mobstand - 1.1.3 + 1.2.0 RS-MobStand diff --git a/src/main/java/com/rsscripting/mobstand/gui/ArmPoseMenu.java b/src/main/java/com/rsscripting/mobstand/gui/ArmPoseMenu.java new file mode 100644 index 0000000..30238dd --- /dev/null +++ b/src/main/java/com/rsscripting/mobstand/gui/ArmPoseMenu.java @@ -0,0 +1,372 @@ +package com.rsscripting.mobstand.gui; + +import com.rsscripting.mobstand.RSMobStandPlugin; +import com.rsscripting.mobstand.utils.ArmPoseUtils; +import com.rsscripting.mobstand.utils.RSMenuUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.List; + +public final class ArmPoseMenu { + + public static final String MENU_ID = + "ARMPOSE"; + + private ArmPoseMenu() { + // Utility-style menu class + } + + public static void open( + RSMobStandPlugin plugin, + Player player, + ArmorStand armorStand + ) { + + Inventory inventory = + Bukkit.createInventory( + new RSMenuHolder(MENU_ID), + 36, + ChatColor.BLUE + "Arm Poses" + ); + + ItemStack filler = + RSMenuUtils.createFillerPane(); + + for (int i = 0; i < inventory.getSize(); i++) { + + inventory.setItem( + i, + filler + ); + } + + /* + |-------------------------------------------------------------------------- + | BACK + |-------------------------------------------------------------------------- + */ + + ItemStack backItem = + new ItemStack( + Material.ARROW + ); + + ItemMeta backMeta = + backItem.getItemMeta(); + + if (backMeta != null) { + + backMeta.setDisplayName( + ChatColor.YELLOW + + "Back" + ); + + backMeta.setLore( + List.of( + ChatColor.GRAY + + "Return to MobStand settings." + ) + ); + + backItem.setItemMeta( + backMeta + ); + } + + inventory.setItem( + 4, + backItem + ); + + /* + |-------------------------------------------------------------------------- + | ARMS DISABLED + |-------------------------------------------------------------------------- + */ + + if (!armorStand.hasArms()) { + + ItemStack disabled = + new ItemStack( + Material.BARRIER + ); + + ItemMeta disabledMeta = + disabled.getItemMeta(); + + if (disabledMeta != null) { + + disabledMeta.setDisplayName( + ChatColor.RED + + "Arms Disabled" + ); + + disabledMeta.setLore( + List.of( + ChatColor.GRAY + + "Enable Arms from the", + ChatColor.GRAY + + "main MobStand menu first." + ) + ); + + disabled.setItemMeta( + disabledMeta + ); + } + + inventory.setItem( + 13, + disabled + ); + + inventory.setItem( + 22, + disabled.clone() + ); + + player.openInventory( + inventory + ); + + return; + } + + /* + |-------------------------------------------------------------------------- + | MAIN HAND + |-------------------------------------------------------------------------- + */ + + inventory.setItem( + 9, + createLabel( + Material.IRON_SWORD, + "Main Hand" + ) + ); + + ArmPoseUtils.Pose mainHandPose = + ArmPoseUtils.getMainHandPose( + armorStand + ); + + inventory.setItem( + 10, + createPoseItem( + "Down", + ArmPoseUtils.Pose.DOWN, + mainHandPose + ) + ); + + inventory.setItem( + 11, + createPoseItem( + "1/4 Down", + ArmPoseUtils.Pose.QUARTER_DOWN, + mainHandPose + ) + ); + + inventory.setItem( + 12, + createPoseItem( + "Straight Out", + ArmPoseUtils.Pose.STRAIGHT_OUT, + mainHandPose + ) + ); + + inventory.setItem( + 13, + createPoseItem( + "1/4 Up", + ArmPoseUtils.Pose.QUARTER_UP, + mainHandPose + ) + ); + + inventory.setItem( + 14, + createPoseItem( + "Straight Up", + ArmPoseUtils.Pose.STRAIGHT_UP, + mainHandPose + ) + ); + + /* + |-------------------------------------------------------------------------- + | OFF HAND + |-------------------------------------------------------------------------- + */ + + inventory.setItem( + 18, + createLabel( + Material.SHIELD, + "Off Hand" + ) + ); + + ArmPoseUtils.Pose offHandPose = + ArmPoseUtils.getOffHandPose( + armorStand + ); + + inventory.setItem( + 19, + createPoseItem( + "Down", + ArmPoseUtils.Pose.DOWN, + offHandPose + ) + ); + + inventory.setItem( + 20, + createPoseItem( + "1/4 Down", + ArmPoseUtils.Pose.QUARTER_DOWN, + offHandPose + ) + ); + + inventory.setItem( + 21, + createPoseItem( + "Straight Out", + ArmPoseUtils.Pose.STRAIGHT_OUT, + offHandPose + ) + ); + + inventory.setItem( + 22, + createPoseItem( + "1/4 Up", + ArmPoseUtils.Pose.QUARTER_UP, + offHandPose + ) + ); + + inventory.setItem( + 23, + createPoseItem( + "Straight Up", + ArmPoseUtils.Pose.STRAIGHT_UP, + offHandPose + ) + ); + + player.openInventory( + inventory + ); + } + + private static ItemStack createLabel( + Material material, + String name + ) { + + ItemStack item = + new ItemStack( + material + ); + + ItemMeta meta = + item.getItemMeta(); + + if (meta != null) { + + meta.setDisplayName( + ChatColor.AQUA + + name + ); + + meta.setLore( + List.of( + ChatColor.GRAY + + "Select an arm pose below." + ) + ); + + meta.addItemFlags( + ItemFlag.HIDE_ATTRIBUTES + ); + + item.setItemMeta( + meta + ); + } + + return item; + } + + private static ItemStack createPoseItem( + String name, + ArmPoseUtils.Pose pose, + ArmPoseUtils.Pose current + ) { + + ItemStack item = + new ItemStack( + Material.ARMOR_STAND + ); + + ItemMeta meta = + item.getItemMeta(); + + if (meta != null) { + + boolean selected = + pose == current; + + meta.setDisplayName( + selected + ? ChatColor.GREEN + name + : ChatColor.YELLOW + name + ); + + meta.setLore( + List.of( + selected + ? ChatColor.GREEN + + "Currently selected." + : ChatColor.GRAY + + "Click to select." + ) + ); + + if (selected) { + + meta.addEnchant( + Enchantment.UNBREAKING, + 1, + true + ); + + meta.addItemFlags( + ItemFlag.HIDE_ENCHANTS, + ItemFlag.HIDE_ATTRIBUTES + ); + } + + item.setItemMeta( + meta + ); + } + + return item; + } +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/mobstand/gui/RSAdminMenu.java b/src/main/java/com/rsscripting/mobstand/gui/RSAdminMenu.java index d029a50..787c940 100644 --- a/src/main/java/com/rsscripting/mobstand/gui/RSAdminMenu.java +++ b/src/main/java/com/rsscripting/mobstand/gui/RSAdminMenu.java @@ -182,9 +182,10 @@ public class RSAdminMenu { ); } - else if (!currentVersion.equalsIgnoreCase( + else if (VersionChecker.compareVersions( + currentVersion, latestVersion - )) { + ) < 0) { lore.add( ChatColor.GRAY diff --git a/src/main/java/com/rsscripting/mobstand/gui/StandMenu.java b/src/main/java/com/rsscripting/mobstand/gui/StandMenu.java index 5781712..86796de 100644 --- a/src/main/java/com/rsscripting/mobstand/gui/StandMenu.java +++ b/src/main/java/com/rsscripting/mobstand/gui/StandMenu.java @@ -34,7 +34,7 @@ public class StandMenu { new RSMenuHolder( "MAIN" ), - 27, + 36, MENU_TITLE ); @@ -185,6 +185,44 @@ public class StandMenu { inventory.setItem(6, pauseItem); +// View Radius item + ItemStack viewRadiusItem = + new ItemStack( + Material.ENDER_EYE + ); + + ItemMeta viewRadiusMeta = + viewRadiusItem.getItemMeta(); + + if (viewRadiusMeta != null) { + + viewRadiusMeta.setDisplayName( + ChatColor.GREEN + + "View Radius" + ); + + viewRadiusMeta.setLore(List.of( + ChatColor.GRAY + + "Display the current scan area.", + ChatColor.GRAY + + "Shows horizontal radius", + ChatColor.GRAY + + "and vertical scan height.", + "", + ChatColor.YELLOW + + "Click to view." + )); + + viewRadiusItem.setItemMeta( + viewRadiusMeta + ); + } + + inventory.setItem( + 31, + viewRadiusItem + ); + @@ -238,6 +276,47 @@ public class StandMenu { inventory.setItem(17, babyFilters); + // Display Name item + ItemStack displayNameItem = + new ItemStack(Material.NAME_TAG); + + ItemMeta displayNameMeta = + displayNameItem.getItemMeta(); + + if (displayNameMeta != null) { + + boolean visible = + armorStand.isCustomNameVisible(); + + displayNameMeta.setDisplayName( + (visible + ? ChatColor.GREEN + : ChatColor.RED) + + "Display Name" + ); + + displayNameMeta.setLore(List.of( + ChatColor.GRAY + "Show or hide the", + ChatColor.GRAY + "RS MobStand name.", + "", + ChatColor.GRAY + "Current: " + + (visible + ? ChatColor.GREEN + "Visible" + : ChatColor.RED + "Hidden"), + "", + ChatColor.YELLOW + "Click to toggle." + )); + + displayNameItem.setItemMeta( + displayNameMeta + ); + } + + inventory.setItem( + 20, + displayNameItem + ); + // Leave Alive item int leaveAlive = @@ -355,7 +434,7 @@ public class StandMenu { inventory.setItem(11, gravityItem); -// Arms toggle +// Arm Poses ItemStack armsItem = new ItemStack( Material.IRON_SWORD @@ -367,21 +446,28 @@ public class StandMenu { if (armsMeta != null) { armsMeta.setDisplayName( - (arms - ? ChatColor.GREEN - : ChatColor.RED) - + "Arms" + arms + ? ChatColor.GREEN + "Arm Poses" + : ChatColor.RED + "Arm Poses" ); armsMeta.setLore(List.of( - ChatColor.GRAY + "Current: " + ChatColor.GRAY + "Configure the Main Hand", + ChatColor.GRAY + "and Off Hand arm poses.", + "", + ChatColor.GRAY + "Arms: " + (arms ? ChatColor.GREEN + "Enabled" : ChatColor.RED + "Disabled"), "", - ChatColor.YELLOW + "Click to toggle." + ChatColor.YELLOW + + "Click to configure." )); + armsMeta.addItemFlags( + ItemFlag.HIDE_ATTRIBUTES + ); + if (arms) { armsMeta.addEnchant( @@ -391,17 +477,19 @@ public class StandMenu { ); armsMeta.addItemFlags( - ItemFlag.HIDE_ENCHANTS, - ItemFlag.HIDE_ATTRIBUTES + ItemFlag.HIDE_ENCHANTS ); - } - armsItem.setItemMeta(armsMeta); - + armsItem.setItemMeta( + armsMeta + ); } - inventory.setItem(22, armsItem); + inventory.setItem( + 22, + armsItem + ); player.openInventory(inventory); diff --git a/src/main/java/com/rsscripting/mobstand/listeners/MenuListener.java b/src/main/java/com/rsscripting/mobstand/listeners/MenuListener.java index 0911145..ae12e14 100644 --- a/src/main/java/com/rsscripting/mobstand/listeners/MenuListener.java +++ b/src/main/java/com/rsscripting/mobstand/listeners/MenuListener.java @@ -5,7 +5,9 @@ import com.rsscripting.mobstand.data.MobFilterData; import com.rsscripting.mobstand.gui.*; import com.rsscripting.mobstand.managers.StandManager; import com.rsscripting.mobstand.gui.RSAdminMenu; +import com.rsscripting.mobstand.utils.ArmPoseUtils; +import com.rsscripting.mobstand.utils.RadiusVisualizer; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.entity.ArmorStand; @@ -691,7 +693,157 @@ public class MenuListener implements Listener { } -// Stand Menu +/* ========================================================= + ARM POSE MENU + ========================================================= */ + + if (event.getInventory() + .getHolder() + instanceof RSMenuHolder holder + && holder.getMenuId() + .equals( + "ARMPOSE" + )) { + + event.setCancelled(true); + + UUID standUUID = + standManager.getSelectedArmorStand( + player.getUniqueId() + ); + + if (standUUID == null) { + player.closeInventory(); + return; + } + + ArmorStand armorStand = + (ArmorStand) Bukkit.getEntity( + standUUID + ); + + if (armorStand == null) { + player.closeInventory(); + return; + } + + int slot = event.getRawSlot(); + + // Back button + if (slot == 4) { + + StandMenu.open( + plugin, + player, + armorStand + ); + + return; + } + + // Arms must be enabled + if (!armorStand.hasArms()) { + + player.sendMessage( + ChatColor.RED + + "Enable arms before configuring arm poses." + ); + + return; + } + + // Main Hand + if (slot == 10) { + + ArmPoseUtils.setMainHandPose( + armorStand, + ArmPoseUtils.Pose.DOWN + ); + + } else if (slot == 11) { + + ArmPoseUtils.setMainHandPose( + armorStand, + ArmPoseUtils.Pose.QUARTER_DOWN + ); + + } else if (slot == 12) { + + ArmPoseUtils.setMainHandPose( + armorStand, + ArmPoseUtils.Pose.STRAIGHT_OUT + ); + + } else if (slot == 13) { + + ArmPoseUtils.setMainHandPose( + armorStand, + ArmPoseUtils.Pose.QUARTER_UP + ); + + } else if (slot == 14) { + + ArmPoseUtils.setMainHandPose( + armorStand, + ArmPoseUtils.Pose.STRAIGHT_UP + ); + + // Off Hand + } else if (slot == 19) { + + ArmPoseUtils.setOffHandPose( + armorStand, + ArmPoseUtils.Pose.DOWN + ); + + } else if (slot == 20) { + + ArmPoseUtils.setOffHandPose( + armorStand, + ArmPoseUtils.Pose.QUARTER_DOWN + ); + + } else if (slot == 21) { + + ArmPoseUtils.setOffHandPose( + armorStand, + ArmPoseUtils.Pose.STRAIGHT_OUT + ); + + } else if (slot == 22) { + + ArmPoseUtils.setOffHandPose( + armorStand, + ArmPoseUtils.Pose.QUARTER_UP + ); + + } else if (slot == 23) { + + ArmPoseUtils.setOffHandPose( + armorStand, + ArmPoseUtils.Pose.STRAIGHT_UP + ); + + } else { + + return; + } + + // Refresh the Arm Poses menu + ArmPoseMenu.open( + plugin, + player, + armorStand + ); + + return; + } + + +/* ========================================================= + STAND MENU + ========================================================= */ + if (event.getInventory() .getHolder() instanceof RSMenuHolder holder @@ -829,44 +981,16 @@ public class MenuListener implements Listener { } -// Arms toggle - if (event.getSlot() == 22) { + // Display Name toggle + if (event.getSlot() == 20) { - boolean mainHandOccupied = - !armorStand.getEquipment() - .getItemInMainHand() - .getType() - .isAir(); + boolean visible = + armorStand.isCustomNameVisible(); - boolean offHandOccupied = - !armorStand.getEquipment() - .getItemInOffHand() - .getType() - .isAir(); + visible = !visible; - if (mainHandOccupied || offHandOccupied) { - - player.sendMessage( - ChatColor.RED - + "You must remove all items from the MobStand's hands before disabling its arms." - ); - - return; - } - - boolean arms = - armorStand.hasArms(); - - arms = !arms; - - armorStand.setArms(arms); - - player.sendMessage( - arms - ? ChatColor.GREEN - + "MobStand arms added." - : ChatColor.GREEN - + "MobStand arms removed." + armorStand.setCustomNameVisible( + visible ); StandMenu.open( @@ -878,6 +1002,28 @@ public class MenuListener implements Listener { return; } +// Arm Poses + if (event.getSlot() == 22) { + + if (!armorStand.hasArms()) { + + armorStand.setArms(true); + + player.sendMessage( + ChatColor.GREEN + + "MobStand arms enabled." + ); + } + + ArmPoseMenu.open( + plugin, + player, + armorStand + ); + + return; + } + // Open leave Alive Menu if (event.getSlot() == 13) { @@ -906,6 +1052,26 @@ public class MenuListener implements Listener { return; } + // View Radius + if (event.getSlot() == 31) { + + double radius = + standManager.getRadius( + armorStand + ); + + player.closeInventory(); + + RadiusVisualizer.show( + plugin, + player, + armorStand, + radius + ); + + return; + } + // Gravity toggle if (event.getSlot() == 11) { diff --git a/src/main/java/com/rsscripting/mobstand/utils/ArmPoseUtils.java b/src/main/java/com/rsscripting/mobstand/utils/ArmPoseUtils.java new file mode 100644 index 0000000..016d83a --- /dev/null +++ b/src/main/java/com/rsscripting/mobstand/utils/ArmPoseUtils.java @@ -0,0 +1,174 @@ +package com.rsscripting.mobstand.utils; + +import org.bukkit.entity.ArmorStand; +import org.bukkit.util.EulerAngle; + +public final class ArmPoseUtils { + + private ArmPoseUtils() { + // Utility class + } + + public enum Pose { + DOWN, + QUARTER_DOWN, + STRAIGHT_OUT, + QUARTER_UP, + STRAIGHT_UP + } + + public static void setMainHandPose( + ArmorStand armorStand, + Pose pose + ) { + + armorStand.setRightArmPose( + getRightArmPose(pose) + ); + } + + public static void setOffHandPose( + ArmorStand armorStand, + Pose pose + ) { + + armorStand.setLeftArmPose( + getLeftArmPose(pose) + ); + } + + public static Pose getMainHandPose( + ArmorStand armorStand + ) { + + return findClosestPose( + armorStand.getRightArmPose() + ); + } + + public static Pose getOffHandPose( + ArmorStand armorStand + ) { + + return findClosestPose( + armorStand.getLeftArmPose() + ); + } + + private static EulerAngle getRightArmPose( + Pose pose + ) { + + return switch (pose) { + + case DOWN -> + new EulerAngle( + 0, + 0, + 0 + ); + + case QUARTER_DOWN -> + new EulerAngle( + Math.toRadians(-45), + 0, + 0 + ); + + case STRAIGHT_OUT -> + new EulerAngle( + Math.toRadians(-90), + 0, + 0 + ); + + case QUARTER_UP -> + new EulerAngle( + Math.toRadians(-135), + 0, + 0 + ); + + case STRAIGHT_UP -> + new EulerAngle( + Math.toRadians(-180), + 0, + 0 + ); + }; + } + + private static EulerAngle getLeftArmPose( + Pose pose + ) { + + return switch (pose) { + + case DOWN -> + new EulerAngle( + 0, + 0, + 0 + ); + + case QUARTER_DOWN -> + new EulerAngle( + Math.toRadians(-45), + 0, + 0 + ); + + case STRAIGHT_OUT -> + new EulerAngle( + Math.toRadians(-90), + 0, + 0 + ); + + case QUARTER_UP -> + new EulerAngle( + Math.toRadians(-135), + 0, + 0 + ); + + case STRAIGHT_UP -> + new EulerAngle( + Math.toRadians(-180), + 0, + 0 + ); + }; + } + + private static Pose findClosestPose( + EulerAngle actual + ) { + + Pose closest = + Pose.DOWN; + + double closestDistance = + Double.MAX_VALUE; + + for (Pose pose : Pose.values()) { + + EulerAngle target = + getRightArmPose(pose); + + double distance = + Math.abs( + actual.getX() + - target.getX() + ); + + if (distance < closestDistance) { + + closestDistance = distance; + closest = pose; + } + } + + return closest; + } +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/mobstand/utils/RadiusVisualizer.java b/src/main/java/com/rsscripting/mobstand/utils/RadiusVisualizer.java new file mode 100644 index 0000000..138aeff --- /dev/null +++ b/src/main/java/com/rsscripting/mobstand/utils/RadiusVisualizer.java @@ -0,0 +1,362 @@ +package com.rsscripting.mobstand.utils; + +import com.rsscripting.mobstand.RSMobStandPlugin; +import org.bukkit.Color; +import org.bukkit.Location; +import org.bukkit.Particle; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +public final class RadiusVisualizer { + + private RadiusVisualizer() { + // Utility class + } + + public static void show( + RSMobStandPlugin plugin, + Player player, + ArmorStand armorStand, + double radius + ) { + + Location center = + armorStand.getLocation().clone(); + + new BukkitRunnable() { + + int ticks = 0; + + @Override + public void run() { + + if (!player.isOnline() + || armorStand.isDead()) { + + cancel(); + return; + } + + drawOutline( + player, + center, + radius + ); + + ticks += 10; + + if (ticks >= 300) { + cancel(); + } + } + + }.runTaskTimer( + plugin, + 0L, + 10L + ); + } + + private static void drawOutline( + Player player, + Location center, + double radius + ) { + + double minX = + center.getX() - radius; + + double maxX = + center.getX() + radius; + + double minZ = + center.getZ() - radius; + + double maxZ = + center.getZ() + radius; + + double bottomY = + center.getY() - 2; + + double topY = + center.getY() + 2; + + Particle.DustOptions red = + new Particle.DustOptions( + Color.RED, + 1.0F + ); + + // Bottom square + drawLine( + player, + new Location( + center.getWorld(), + minX, + bottomY, + minZ + ), + new Location( + center.getWorld(), + maxX, + bottomY, + minZ + ), + red + ); + + drawLine( + player, + new Location( + center.getWorld(), + maxX, + bottomY, + minZ + ), + new Location( + center.getWorld(), + maxX, + bottomY, + maxZ + ), + red + ); + + drawLine( + player, + new Location( + center.getWorld(), + maxX, + bottomY, + maxZ + ), + new Location( + center.getWorld(), + minX, + bottomY, + maxZ + ), + red + ); + + drawLine( + player, + new Location( + center.getWorld(), + minX, + bottomY, + maxZ + ), + new Location( + center.getWorld(), + minX, + bottomY, + minZ + ), + red + ); + + // Top square + drawLine( + player, + new Location( + center.getWorld(), + minX, + topY, + minZ + ), + new Location( + center.getWorld(), + maxX, + topY, + minZ + ), + red + ); + + drawLine( + player, + new Location( + center.getWorld(), + maxX, + topY, + minZ + ), + new Location( + center.getWorld(), + maxX, + topY, + maxZ + ), + red + ); + + drawLine( + player, + new Location( + center.getWorld(), + maxX, + topY, + maxZ + ), + new Location( + center.getWorld(), + minX, + topY, + maxZ + ), + red + ); + + drawLine( + player, + new Location( + center.getWorld(), + minX, + topY, + maxZ + ), + new Location( + center.getWorld(), + minX, + topY, + minZ + ), + red + ); + + // Four vertical corners + drawLine( + player, + new Location( + center.getWorld(), + minX, + bottomY, + minZ + ), + new Location( + center.getWorld(), + minX, + topY, + minZ + ), + red + ); + + drawLine( + player, + new Location( + center.getWorld(), + maxX, + bottomY, + minZ + ), + new Location( + center.getWorld(), + maxX, + topY, + minZ + ), + red + ); + + drawLine( + player, + new Location( + center.getWorld(), + maxX, + bottomY, + maxZ + ), + new Location( + center.getWorld(), + maxX, + topY, + maxZ + ), + red + ); + + drawLine( + player, + new Location( + center.getWorld(), + minX, + bottomY, + maxZ + ), + new Location( + center.getWorld(), + minX, + topY, + maxZ + ), + red + ); + } + + private static void drawLine( + Player player, + Location start, + Location end, + Particle.DustOptions dust + ) { + + double distance = + start.distance(end); + + double step = + 0.25; + + int points = + Math.max( + 1, + (int) Math.ceil( + distance / step + ) + ); + + for (int i = 0; i <= points; i++) { + + double progress = + (double) i / points; + + double x = + start.getX() + + ( + end.getX() + - start.getX() + ) * progress; + + double y = + start.getY() + + ( + end.getY() + - start.getY() + ) * progress; + + double z = + start.getZ() + + ( + end.getZ() + - start.getZ() + ) * progress; + + Location particleLocation = + new Location( + start.getWorld(), + x, + y, + z + ); + + player.spawnParticle( + Particle.DUST, + particleLocation, + 1, + dust + ); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/mobstand/utils/VersionChecker.java b/src/main/java/com/rsscripting/mobstand/utils/VersionChecker.java index 1668e7e..0cb351b 100644 --- a/src/main/java/com/rsscripting/mobstand/utils/VersionChecker.java +++ b/src/main/java/com/rsscripting/mobstand/utils/VersionChecker.java @@ -88,39 +88,59 @@ public class VersionChecker { plugin.getDescription() .getVersion(); - /* - |-------------------------------------------------------------------------- - | UPDATE AVAILABLE - |-------------------------------------------------------------------------- - */ +/* +|-------------------------------------------------------------------------- +| VERSION COMPARISON +|-------------------------------------------------------------------------- +*/ - if (!latestVersion.equalsIgnoreCase( - "Unknown" - ) && !currentVersion.equalsIgnoreCase( - latestVersion - )) { + if (!latestVersion.equalsIgnoreCase("Unknown")) { - plugin.getLogger().warning( - "=================================================" - ); + int comparison = + compareVersions( + currentVersion, + latestVersion + ); - plugin.getLogger().warning( - "A new version of RS-MobStand is available!" - ); + /* + * Current version is older than GitHub version. + */ + if (comparison < 0) { - plugin.getLogger().warning( - "Current Version: " - + currentVersion - ); + plugin.getLogger().warning( + "=================================================" + ); - plugin.getLogger().warning( - "Latest Version: " - + latestVersion - ); + plugin.getLogger().warning( + "A new version of RS-MobStand is available!" + ); - plugin.getLogger().warning( - "=================================================" - ); + plugin.getLogger().warning( + "Current Version: " + + currentVersion + ); + + plugin.getLogger().warning( + "Latest Version: " + + latestVersion + ); + + plugin.getLogger().warning( + "=================================================" + ); + + } + + /* + * Current version is equal to or newer than GitHub version. + */ + else { + + plugin.getLogger().info( + "RS-MobStand is up to date." + ); + + } } @@ -160,6 +180,55 @@ public class VersionChecker { } + public static int compareVersions( + String currentVersion, + String latestVersion + ) { + + String[] currentParts = + currentVersion + .replaceAll("[^0-9.]", "") + .split("\\."); + + String[] latestParts = + latestVersion + .replaceAll("[^0-9.]", "") + .split("\\."); + + int length = + Math.max( + currentParts.length, + latestParts.length + ); + + for (int i = 0; i < length; i++) { + + int current = + i < currentParts.length + ? Integer.parseInt( + currentParts[i] + ) + : 0; + + int latest = + i < latestParts.length + ? Integer.parseInt( + latestParts[i] + ) + : 0; + + if (current < latest) { + return -1; + } + + if (current > latest) { + return 1; + } + } + + return 0; + } + public static String getLatestVersion() { return latestVersion;