diff --git a/src/main/java/me/trouper/trimserver/server/commands/AdminCommand.java b/src/main/java/me/trouper/trimserver/server/commands/AdminCommand.java index 38ba917..d775902 100755 --- a/src/main/java/me/trouper/trimserver/server/commands/AdminCommand.java +++ b/src/main/java/me/trouper/trimserver/server/commands/AdminCommand.java @@ -26,11 +26,13 @@ import org.bukkit.inventory.meta.trim.ArmorTrim; import org.bukkit.inventory.meta.trim.TrimMaterial; import org.bukkit.inventory.meta.trim.TrimPattern; -import java.util.List; +import java.util.*; @CommandRegistry(value = "trims",permission = @Permission("trims.admin"),printStackTrace = true) public class AdminCommand implements QuickCommand { + public static final Set adminBypass = new HashSet<>(); + @Override public void dispatchCommand(CommandSender commandSender, Command command, String s, Args args) { if (args.getSize() < 1) { @@ -42,13 +44,14 @@ public class AdminCommand implements QuickCommand { case "try" -> handleTry(commandSender,args); case "test" -> handleTest(commandSender,args); case "give" -> handleGive(commandSender,args); + case "bypass" -> handleBypass(commandSender,args); } } @Override public void dispatchCompletions(CommandSender commandSender, Command command, String s, CompletionBuilder b) { b.then( - b.arg("reload") + b.arg("reload","bypass") ).then( b.arg("debug") .then( @@ -104,6 +107,18 @@ public class AdminCommand implements QuickCommand { ); } + private void handleBypass(CommandSender sender, Args args) { + if (!(sender instanceof Player p)) { + error(sender,"This sub-command is player only."); + return; + } + if (adminBypass.remove(p.getUniqueId())) { + success(sender, "You are {0} bypassing ability restrictions.","now"); + } else if (adminBypass.add(p.getUniqueId())) { + success(sender, "You are {0} bypassing ability restrictions.","no longer"); + } + } + private void handleGive(CommandSender commandSender, Args args) { switch (args.get(1).toString()) { case "consumable" -> { diff --git a/src/main/java/me/trouper/trimserver/server/systems/AbilityBackend.java b/src/main/java/me/trouper/trimserver/server/systems/AbilityBackend.java index 6119245..6faa3af 100755 --- a/src/main/java/me/trouper/trimserver/server/systems/AbilityBackend.java +++ b/src/main/java/me/trouper/trimserver/server/systems/AbilityBackend.java @@ -10,6 +10,7 @@ import io.papermc.paper.registry.RegistryAccess; import io.papermc.paper.registry.RegistryKey; import me.trouper.trimserver.server.Main; import me.trouper.trimserver.server.api.AbilityFlag; +import me.trouper.trimserver.server.commands.AdminCommand; import me.trouper.trimserver.server.systems.abilities.MaterialInfo; import me.trouper.trimserver.server.systems.abilities.AbstractAbility; import me.trouper.trimserver.utils.text.Text; @@ -285,13 +286,12 @@ public class AbilityBackend implements Main { AbilityCooldown cooldownKey = new AbilityCooldown(player.getUniqueId(), info.getValidPattern().getCanonical(), info.getValidMaterial().getCanonical()); - if (onCooldown.isOnCooldown(cooldownKey)) return false; - + if (!AdminCommand.adminBypass.contains(player.getUniqueId()) && onCooldown.isOnCooldown(cooldownKey)) return false; try { boolean applyCooldown = abilityAllowed(player, player.getLocation()) && ability.dispatchAbility(info.getValidMaterial(), player); - if (cooldownTicks > 0 && applyCooldown) onCooldown.addCooldown(cooldownKey, (long) cooldownTicks * 50L); + if (cooldownTicks > 0 && applyCooldown) onCooldown.setCooldown(cooldownKey, (long) cooldownTicks * 50L); return true; } catch (Exception e) { @@ -346,6 +346,7 @@ public class AbilityBackend implements Main { * @return true if the ability is allowed, false otherwise */ public boolean abilityAllowed(Player player, Location useLocation) { + if (AdminCommand.adminBypass.contains(player.getUniqueId())) return true; LocalPlayer user = WorldGuardPlugin.inst().wrapPlayer(player); RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer(); diff --git a/src/main/java/me/trouper/trimserver/server/systems/PollingBackend.java b/src/main/java/me/trouper/trimserver/server/systems/PollingBackend.java index ec9c2be..d1e564e 100644 --- a/src/main/java/me/trouper/trimserver/server/systems/PollingBackend.java +++ b/src/main/java/me/trouper/trimserver/server/systems/PollingBackend.java @@ -1,11 +1,13 @@ package me.trouper.trimserver.server.systems; import me.trouper.trimserver.server.Main; +import me.trouper.trimserver.server.commands.AdminCommand; import me.trouper.trimserver.server.systems.abilities.AbstractAbility; import me.trouper.trimserver.server.systems.abilities.MaterialInfo; import me.trouper.trimserver.utils.text.Text; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.inventory.meta.trim.TrimMaterial; @@ -52,14 +54,16 @@ public class PollingBackend implements Main { elapsedMillis = Math.max(0, elapsedMillis); elapsedMillis = Math.min(totalDurationMillis, elapsedMillis); - bar = bar.append(Component.text("Ability Recharging: ", NamedTextColor.WHITE)) + bar = bar.append(Component.text("Ability Recharging: ", TextColor.color(0xFFAAAA))) .append(Text.color(Text.generateProgressBar(20, (int)totalDurationMillis, (int)elapsedMillis))); } else { - bar = bar.append(Component.text("Ability Ready", NamedTextColor.namedColor(0x00FFAA))); + bar = bar.append(Component.text("Ability Ready", TextColor.color(0x00FFAA))); } - bar = bar.append(Text.formatArgs(Text.Pallet.INFO," /info {0} for usage",Text.formatEnum(AbilityBackend.ValidPattern.validate(pattern)))); + bar = bar.append(Text.formatArgs(Text.Pallet.INFO," /triminfo {0} for usage",Text.formatEnum(AbilityBackend.ValidPattern.validate(pattern)))); + if (AdminCommand.adminBypass.contains(player.getUniqueId())) bar = bar.append(Component.text(" (Admin Mode)",TextColor.color(0x99ffaa))); + player.sendActionBar(bar); } } diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/BoltAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/BoltAbility.java index 3435903..2b4ffe3 100755 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/BoltAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/BoltAbility.java @@ -7,30 +7,33 @@ import me.trouper.trimserver.server.systems.abilities.PatternInfo; import me.trouper.trimserver.utils.PlayerUtils; import me.trouper.trimserver.utils.SoundPlayer; import me.trouper.trimserver.utils.TargetingUtils; +import me.trouper.trimserver.utils.misc.Randomizer; import me.trouper.trimserver.utils.text.Text; import me.trouper.trimserver.utils.visual.BlockDisplayRaytracer; import org.bukkit.*; import org.bukkit.damage.DamageSource; import org.bukkit.damage.DamageType; +import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.inventory.meta.trim.TrimPattern; import org.bukkit.util.Vector; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; @PatternInfo(name = "Bolt", description = "Summon a bolt of lightning at enemies. Includes Variants.") public class BoltAbility extends AbstractAbility implements Main { public static final int NORMAL_COOLDOWN = 20 * 10; - public static final double NORMAL_DAMAGE = 6; + public static final double NORMAL_DAMAGE = 4; - public static final int RESIN_COOLDOWN = 20 * 5; - public static final double RESIN_DAMAGE = 4; + public static final int RESIN_COOLDOWN = 20 * 7; + public static final double RESIN_DAMAGE = 3; public static final int NETHERITE_COOLDOWN = 20 * 15; - public static final double NETHERITE_DAMAGE = 8; + public static final double NETHERITE_DAMAGE = 5; public BoltAbility() { super(TrimPattern.BOLT); @@ -39,48 +42,54 @@ public class BoltAbility extends AbstractAbility implements Main { public boolean strike(Player caster, int range, double damage, Material innerBlock, Material outerBlock) { AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER); ShaperAbility shaper = (ShaperAbility) shaperInstance; - - return TargetingUtils.areaAffect(caster.getLocation(),range,target-> !main.man().trustBackend.trusts(caster,target) && + + Optional hit = TargetingUtils.livingClosestAngle(caster.getEyeLocation(),caster.getEyeLocation().getDirection(),20,1, target-> !main.man().trustBackend.trusts(caster,target) && !shaper.activeShellTasks.containsKey(target.getUniqueId()) && - main.man().abilityBackend.abilityAllowed(caster,target.getLocation()),(target) ->{ - PlayerUtils.dealTrueDamage(target,DamageSource.builder(DamageType.LIGHTNING_BOLT).withDamageLocation(caster.getEyeLocation()).build(),damage); - if (target instanceof Player t) Text.sendMessage(Text.Pallet.INFO,t,"You have been stunned by {0}'s Bolt!",caster.getName()); + main.man().abilityBackend.abilityAllowed(caster,target.getLocation())); + + if (hit.isEmpty()) return false; + + LivingEntity target = hit.get(); + PlayerUtils.dealTrueDamage(target,DamageSource.builder(DamageType.LIGHTNING_BOLT).withDamageLocation(caster.getEyeLocation()).build(),damage); + + if (target instanceof Player t) Text.sendMessage(Text.Pallet.INFO,t,"You have been stunned by {0}'s Bolt!",caster.getName()); - SoundPlayer bolt = new SoundPlayer(target.getLocation(), Sound.ENTITY_LIGHTNING_BOLT_THUNDER,10,1); - SoundPlayer ring = new SoundPlayer(target.getLocation(), Sound.ITEM_TRIDENT_THUNDER,10,2); - SoundPlayer zip = new SoundPlayer(target.getLocation(), Sound.ENTITY_BEE_STING,10,1); + SoundPlayer bolt = new SoundPlayer(target.getLocation(), Sound.ENTITY_LIGHTNING_BOLT_THUNDER,10,1); + SoundPlayer ring = new SoundPlayer(target.getLocation(), Sound.ITEM_TRIDENT_THUNDER,10,1.5F); + SoundPlayer zip = new SoundPlayer(target.getLocation(), Sound.ENTITY_BEE_DEATH,10,2); - bolt.playWithin(50); - ring.playWithin(30); + bolt.playWithin(50); + ring.playWithin(30); - AtomicInteger counter = new AtomicInteger(0); - Bukkit.getScheduler().runTaskTimer(main.getPlugin(),(task) -> { - if (counter.getAndIncrement() > 40) { - task.cancel(); - return; - } - int tick = target.getNoDamageTicks(); - int maxTick = target.getMaximumNoDamageTicks(); + AtomicInteger counter = new AtomicInteger(0); + Bukkit.getScheduler().runTaskTimer(main.getPlugin(),(task) -> { + if (counter.getAndIncrement() > 50) { + task.cancel(); + return; + } + int tick = target.getNoDamageTicks(); + int maxTick = target.getMaximumNoDamageTicks(); - if (counter.get() % 5 == 0) zip.playWithin(30); - target.setNoDamageTicks(0); - target.setMaximumNoDamageTicks(1); - target.damage(0.01,DamageSource.builder(DamageType.LIGHTNING_BOLT).withDamageLocation(caster.getLocation()).withDirectEntity(caster).build()); - target.setNoDamageTicks(tick); - target.setMaximumNoDamageTicks(maxTick); - Location stunLoc = target.getLocation().clone(); - target.teleport(stunLoc); - drawLightning(caster.getEyeLocation().subtract(0,0.5,0),target.getEyeLocation(),innerBlock,outerBlock); - },0,1); - }); + if (counter.get() % 5 == 0) zip.playWithin(30); + target.setNoDamageTicks(0); + target.setMaximumNoDamageTicks(1); + target.damage(0.01,DamageSource.builder(DamageType.LIGHTNING_BOLT).withDamageLocation(caster.getLocation()).withDirectEntity(caster).build()); + target.setNoDamageTicks(tick); + target.setMaximumNoDamageTicks(maxTick); + Location stunLoc = target.getLocation().clone(); + target.teleport(stunLoc); + drawLightning(caster.getEyeLocation().subtract(0,0.5,0),target.getEyeLocation(),innerBlock,outerBlock); + },0,1); + + return true; } public void drawLightning(Location start, Location end, Material blockInner, Material blockOuter) { int segments = 10; double maxOffset = 0.5; long stayTime = 10L; - double thickness = 0.01; - double thicknessOut = 0.05; + double thickness = 0.02; + double thicknessOut = 0.04; List viewers = new ArrayList<>(start.getWorld().getPlayers()); Location current = start.clone(); @@ -98,75 +107,75 @@ public class BoltAbility extends AbstractAbility implements Main { BlockDisplayRaytracer.trace(blockInner, current, next, thickness, stayTime, viewers); BlockDisplayRaytracer.trace(blockOuter, current, next, thicknessOut, stayTime, viewers); - next.getWorld().spawnParticle(Particle.FLASH,next,0,0,0,0,0); current = next; } - + BlockDisplayRaytracer.trace(blockInner, current, end, thickness, stayTime, viewers); BlockDisplayRaytracer.trace(blockOuter, current, end, thicknessOut, stayTime, viewers); + end.getWorld().spawnParticle(Particle.FLASH,end,0,0,0,0,0); } - @MaterialInfo(name = "Amethyst Bolt",description = "Shoots a bolt of colored lightning at your closest enemy within 20 blocks. Deals 10 Damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Amethyst Bolt",description = "Shoots a bolt of colored lightning at your closest enemy within 20 blocks. Deals 4 true Damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean amethystAbility(Player player) { return strike(player,20,NORMAL_DAMAGE,Material.AMETHYST_BLOCK,Material.PURPLE_STAINED_GLASS); } - @MaterialInfo(name = "Copper Bolt",description = "Shoots a bolt of colored lightning at your closest enemy within 20 blocks. Deals 10 Damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Copper Bolt",description = "Shoots a bolt of colored lightning at your closest enemy within 20 blocks. Deals 4 true damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean copperAbility(Player player) { return strike(player,20,NORMAL_DAMAGE,Material.ORANGE_TERRACOTTA,Material.ORANGE_STAINED_GLASS); } - @MaterialInfo(name = "Diamond Bolt",description = "Shoots a bolt of colored lightning at your closest enemy within 20 blocks. Deals 10 Damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Diamond Bolt",description = "Shoots a bolt of colored lightning at your closest enemy within 20 blocks. Deals 4 true damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean diamondAbility(Player player) { return strike(player,20,NORMAL_DAMAGE,Material.LIGHT_BLUE_CONCRETE_POWDER,Material.LIGHT_BLUE_STAINED_GLASS); } - @MaterialInfo(name = "Emerald ",description = "Shoots a bolt of colored lightning at your closest enemy within 20 blocks. Deals 10 Damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Emerald ",description = "Shoots a bolt of colored lightning at your closest enemy within 20 blocks. Deals 4 true damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean emeraldAbility(Player player) { return strike(player,20,NORMAL_DAMAGE,Material.LIME_CONCRETE,Material.LIME_STAINED_GLASS); } - @MaterialInfo(name = "Gold Bolt",description = "Shoots a bolt of colored lightning at your closest enemy within 20 blocks. Deals 10 Damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Gold Bolt",description = "Shoots a bolt of colored lightning at your closest enemy within 20 blocks. Deals 4 true damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean goldAbility(Player player) { return strike(player,20,NORMAL_DAMAGE,Material.YELLOW_CONCRETE_POWDER,Material.YELLOW_STAINED_GLASS); } - @MaterialInfo(name = "Iron Bolt",description = "Shoots a bolt of colored lightning at your closest enemy within 20 blocks. Deals 10 Damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Iron Bolt",description = "Shoots a bolt of colored lightning at your closest enemy within 20 blocks. Deals 4 true damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean ironAbility(Player player) { return strike(player,20,NORMAL_DAMAGE,Material.LIGHT_GRAY_WOOL,Material.LIGHT_GRAY_STAINED_GLASS); } - @MaterialInfo(name = "Lapis Bolt",description = "Shoots a bolt of colored lightning at your closest enemy within 20 blocks. Deals 10 Damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Lapis Bolt",description = "Shoots a bolt of colored lightning at your closest enemy within 20 blocks. Deals 4 true damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean lapisAbility(Player player) { return strike(player,20,NORMAL_DAMAGE,Material.BLUE_CONCRETE,Material.BLUE_STAINED_GLASS); } - @MaterialInfo(name = "Netherite Bolt",description = "Shoots a bolt of colored lightning at your closest enemy within 20 blocks. Deals 15 Damage", cooldownTicks = NETHERITE_COOLDOWN) + @MaterialInfo(name = "Netherite Bolt",description = "Shoots a bolt of colored lightning at your closest enemy within 20 blocks. Deals 5 true damage", cooldownTicks = NETHERITE_COOLDOWN) @Override public boolean netheriteAbility(Player player) { return strike(player,30,NETHERITE_DAMAGE,Material.BLACK_CONCRETE,Material.GRAY_STAINED_GLASS); } - @MaterialInfo(name = "Quartz Bolt",description = "Shoots a bolt of overcharged lightning at your closest enemy within 20 blocks. Deals 15 Damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Quartz Bolt",description = "Shoots a bolt of overcharged lightning at your closest enemy within 20 blocks. Deals 4 true Damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean quartzAbility(Player player) { return strike(player,20,NORMAL_DAMAGE,Material.WHITE_CONCRETE,Material.WHITE_STAINED_GLASS); } - @MaterialInfo(name = "Redstone Bolt",description = "Shoots a bolt of colored lightning at your closest enemy within 20 blocks. Deals 10 Damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Redstone Bolt",description = "Shoots a bolt of colored lightning at your closest enemy within 20 blocks. Deals 4 true damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean redstoneAbility(Player player) { return strike(player,20,NORMAL_DAMAGE,Material.RED_CONCRETE_POWDER,Material.RED_STAINED_GLASS); } - @MaterialInfo(name = "Resin Bolt",description = "Shoots a bolt of resin lightning at your closest enemy within 20 blocks. Deals 7 Damage", cooldownTicks = RESIN_COOLDOWN) + @MaterialInfo(name = "Resin Bolt",description = "Shoots a bolt of resin lightning at your closest enemy within 20 blocks. Deals 3 true", cooldownTicks = RESIN_COOLDOWN) @Override public boolean resinAbility(Player player) { return strike(player,20,RESIN_DAMAGE,Material.RESIN_BLOCK,Material.ORANGE_STAINED_GLASS); diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/EyeAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/EyeAbility.java index 9326e0d..72c7545 100755 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/EyeAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/EyeAbility.java @@ -5,6 +5,7 @@ import me.trouper.trimserver.server.systems.abilities.AbstractAbility; import me.trouper.trimserver.server.systems.abilities.PatternInfo; import me.trouper.trimserver.utils.PlayerUtils; import me.trouper.trimserver.utils.SoundPlayer; +import me.trouper.trimserver.utils.text.CustomBossBar; import me.trouper.trimserver.utils.visual.BlockDisplayRaytracer; import me.trouper.trimserver.utils.visual.CustomDisplayRaytracer; import org.bukkit.*; @@ -35,7 +36,7 @@ public class EyeAbility extends AbstractAbility { public static final double NETHERITE_DAMAGE_CHANCE = 1.0; public static final int RESIN_COOLDOWN = 20 * 20; - public static final long RESIN_DURATION = 2; + public static final int RESIN_DURATION = 2; public static final double RESIN_DAMAGE = 0.1; public static final double RESIN_DAMAGE_CHANCE = 0.60; @@ -43,10 +44,14 @@ public class EyeAbility extends AbstractAbility { super(TrimPattern.EYE); } - public void eyeLasers(Player player, Material beam,Material glow, long durationSeconds, double damagePerTick, double damageFailChance) { + public void eyeLasers(Player player, Material beam,Material glow, int durationSeconds, double damagePerTick, double damageFailChance) { + + CustomBossBar.showBossBar(main.getPlugin(),player,"Laser Eyes",durationSeconds * 20); + AtomicInteger timer = new AtomicInteger(0); Bukkit.getScheduler().runTaskTimer(main.getPlugin(),(task)->{ if (timer.getAndIncrement() >= durationSeconds * 20) { + CustomBossBar.removeBossBar(player); task.cancel(); return; } @@ -110,77 +115,77 @@ public class EyeAbility extends AbstractAbility { }).getLoc(); } - @MaterialInfo(name = "Amethyst Laser beam", description = "Shoot lasers from the eye on the chestpiece for 5 seconds", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Amethyst Laser beam", description = "Shoot lasers from your eyes", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean amethystAbility(Player player) { eyeLasers(player,Material.PURPLE_CONCRETE_POWDER,Material.MAGENTA_STAINED_GLASS,NORMAL_DURATION,NORMAL_DAMAGE, NORMAL_DAMAGE_CHANCE); return true; } - @MaterialInfo(name = "Copper Laser beam", description = "Shoot lasers from the eye on the chestpiece for 5 seconds", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Copper Laser beam", description = "Shoot lasers from your eyes", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean copperAbility(Player player) { eyeLasers(player,Material.LIME_TERRACOTTA,Material.GREEN_STAINED_GLASS,NORMAL_DURATION,NORMAL_DAMAGE, NORMAL_DAMAGE_CHANCE); return true; } - @MaterialInfo(name = "Diamond Laser beam", description = "Shoot lasers from the eye on the chestpiece for 5 seconds", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Diamond Laser beam", description = "Shoot lasers from your eyes", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean diamondAbility(Player player) { eyeLasers(player,Material.LIGHT_BLUE_CONCRETE_POWDER,Material.LIGHT_BLUE_STAINED_GLASS,NORMAL_DURATION,NORMAL_DAMAGE, NORMAL_DAMAGE_CHANCE); return true; } - @MaterialInfo(name = "Emerald Laser beam", description = "Shoot lasers from the eye on the chestpiece for 5 seconds", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Emerald Laser beam", description = "Shoot lasers from your eyes", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean emeraldAbility(Player player) { eyeLasers(player,Material.LIME_CONCRETE_POWDER,Material.LIME_STAINED_GLASS,NORMAL_DURATION,NORMAL_DAMAGE, NORMAL_DAMAGE_CHANCE); return true; } - @MaterialInfo(name = "Gold Laser beam", description = "Shoot lasers from the eye on the chestpiece for 5 seconds", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Gold Laser beam", description = "Shoot lasers from your eyes", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean goldAbility(Player player) { eyeLasers(player,Material.YELLOW_TERRACOTTA,Material.YELLOW_STAINED_GLASS,NORMAL_DURATION,NORMAL_DAMAGE, NORMAL_DAMAGE_CHANCE); return true; } - @MaterialInfo(name = "Iron Laser beam", description = "Shoot lasers from the eye on the chestpiece for 5 seconds", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Iron Laser beam", description = "Shoot lasers from your eyes", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean ironAbility(Player player) { eyeLasers(player,Material.LIGHT_GRAY_CONCRETE_POWDER,Material.LIGHT_GRAY_STAINED_GLASS,NORMAL_DURATION,NORMAL_DAMAGE, NORMAL_DAMAGE_CHANCE); return true; } - @MaterialInfo(name = "Lapis Laser beam", description = "Shoot lasers from the eye on the chestpiece for 5 seconds", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Lapis Laser beam", description = "Shoot lasers from your eyes", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean lapisAbility(Player player) { eyeLasers(player,Material.BLUE_CONCRETE,Material.BLUE_STAINED_GLASS,NORMAL_DURATION,NORMAL_DAMAGE, NORMAL_DAMAGE_CHANCE); return true; } - @MaterialInfo(name = "Netherite Laser beam", description = "Shoot lasers from the eye on the chestpiece for 10 seconds", cooldownTicks = NETHERITE_COOLDOWN) + @MaterialInfo(name = "Netherite Laser beam", description = "Shoot lasers from your eyes", cooldownTicks = NETHERITE_COOLDOWN) @Override public boolean netheriteAbility(Player player) { eyeLasers(player,Material.BLACK_CONCRETE,Material.BLACK_STAINED_GLASS,NETHERITE_DURATION,NETHERITE_DAMAGE, NETHERITE_DAMAGE_CHANCE); return true; } - @MaterialInfo(name = "Quartz Laser beam", description = "Shoot lasers from the eye on the chestpiece for 5 seconds", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Quartz Laser beam", description = "Shoot lasers from your eyes", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean quartzAbility(Player player) { eyeLasers(player,Material.WHITE_CONCRETE_POWDER,Material.WHITE_STAINED_GLASS,NORMAL_DURATION,NORMAL_DAMAGE, NORMAL_DAMAGE_CHANCE); return true; } - @MaterialInfo(name = "Redstone Laser beam", description = "Shoot lasers from the eye on the chestpiece for 5 seconds", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Redstone Laser beam", description = "Shoot lasers from your eyes", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean redstoneAbility(Player player) { eyeLasers(player,Material.RED_CONCRETE,Material.RED_STAINED_GLASS,NORMAL_DURATION,NORMAL_DAMAGE, NORMAL_DAMAGE_CHANCE); return true; } - @MaterialInfo(name = "Resin Laser beam", description = "Shoot lasers from the eye on the chestpiece for 5 seconds", cooldownTicks = RESIN_COOLDOWN) + @MaterialInfo(name = "Resin Laser beam", description = "Shoot lasers from your eyes", cooldownTicks = RESIN_COOLDOWN) @Override public boolean resinAbility(Player player) { eyeLasers(player,Material.ORANGE_CONCRETE_POWDER,Material.ORANGE_STAINED_GLASS,RESIN_DURATION,RESIN_DAMAGE, RESIN_DAMAGE_CHANCE); diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/HostAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/HostAbility.java index 8521509..79ec824 100755 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/HostAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/HostAbility.java @@ -89,7 +89,7 @@ public class HostAbility extends AbstractAbility { } } - @MaterialInfo(name = "Amethyst ", description = "Grants true invisibility for 6 seconds but makes your attacks weaker", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Amethyst ", description = "Grants true invisibility for 5 seconds but makes your attacks weaker", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean amethystAbility(Player player) { makeInvisible(player,NORMAL_DURATION); @@ -97,7 +97,7 @@ public class HostAbility extends AbstractAbility { return true; } - @MaterialInfo(name = "Copper ", description = "Grants true invisibility for 6 seconds but makes your attacks weaker", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Copper ", description = "Grants true invisibility for 5 seconds but makes your attacks weaker", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean copperAbility(Player player) { makeInvisible(player,NORMAL_DURATION); @@ -105,7 +105,7 @@ public class HostAbility extends AbstractAbility { return true; } - @MaterialInfo(name = "Diamond ", description = "Grants true invisibility for 6 seconds but makes your attacks weaker", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Diamond ", description = "Grants true invisibility for 5 seconds but makes your attacks weaker", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean diamondAbility(Player player) { makeInvisible(player,NORMAL_DURATION); @@ -113,7 +113,7 @@ public class HostAbility extends AbstractAbility { return true; } - @MaterialInfo(name = "Emerald ", description = "Grants true invisibility for 6 seconds but makes your attacks weaker", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Emerald ", description = "Grants true invisibility for 5 seconds but makes your attacks weaker", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean emeraldAbility(Player player) { makeInvisible(player,NORMAL_DURATION); @@ -121,7 +121,7 @@ public class HostAbility extends AbstractAbility { return true; } - @MaterialInfo(name = "Gold ", description = "Grants true invisibility for 6 seconds but makes your attacks weaker", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Gold ", description = "Grants true invisibility for 5 seconds but makes your attacks weaker", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean goldAbility(Player player) { makeInvisible(player,NORMAL_DURATION); @@ -129,7 +129,7 @@ public class HostAbility extends AbstractAbility { return true; } - @MaterialInfo(name = "Iron ", description = "Grants true invisibility for 6 seconds but makes your attacks weaker", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Iron ", description = "Grants true invisibility for 5 seconds but makes your attacks weaker", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean ironAbility(Player player) { makeInvisible(player,NORMAL_DURATION); @@ -137,7 +137,7 @@ public class HostAbility extends AbstractAbility { return true; } - @MaterialInfo(name = "Lapis ", description = "Grants true invisibility for 6 seconds but makes your attacks weaker", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Lapis ", description = "Grants true invisibility for 5 seconds but makes your attacks weaker", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean lapisAbility(Player player) { makeInvisible(player,NORMAL_DURATION); @@ -145,7 +145,7 @@ public class HostAbility extends AbstractAbility { return true; } - @MaterialInfo(name = "Netherite ", description = "Grants true invisibility for 10 seconds and makes your attacks stronger", cooldownTicks = NETHERITE_COOLDOWN) + @MaterialInfo(name = "Netherite ", description = "Grants true invisibility for 8 seconds and makes your attacks stronger", cooldownTicks = NETHERITE_COOLDOWN) @Override public boolean netheriteAbility(Player player) { makeInvisible(player,NETHERITE_DURATION); @@ -153,7 +153,7 @@ public class HostAbility extends AbstractAbility { return true; } - @MaterialInfo(name = "Quartz ", description = "Grants true invisibility for 6 seconds but makes your attacks weaker", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Quartz ", description = "Grants true invisibility for 5 seconds but makes your attacks weaker", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean quartzAbility(Player player) { makeInvisible(player,NORMAL_DURATION); @@ -161,7 +161,7 @@ public class HostAbility extends AbstractAbility { return true; } - @MaterialInfo(name = "Redstone ", description = "Grants true invisibility for 6 seconds but makes your attacks weaker", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Redstone ", description = "Grants true invisibility for 5 seconds but makes your attacks weaker", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean redstoneAbility(Player player) { makeInvisible(player,NORMAL_DURATION); @@ -169,11 +169,10 @@ public class HostAbility extends AbstractAbility { return true; } - @MaterialInfo(name = "Resin ", description = "Grants true invisibility for 6 seconds but makes your attacks weaker", cooldownTicks = RESIN_COOLDOWN) + @MaterialInfo(name = "Resin ", description = "Grants true invisibility for 3 seconds.", cooldownTicks = RESIN_COOLDOWN) @Override public boolean resinAbility(Player player) { - makeInvisible(player,NORMAL_DURATION); - player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,RESIN_DURATION * 10,0,true,false,true)); + makeInvisible(player,RESIN_DURATION); return true; } } diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/RaiserAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/RaiserAbility.java index 495b864..09652c3 100755 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/RaiserAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/RaiserAbility.java @@ -24,13 +24,13 @@ import java.util.UUID; @PatternInfo(name = "Unexpected Levitation", description = "Raiser? I hardly know her!") public class RaiserAbility extends AbstractAbility { - public static final int NORMAL_DURATION = 15; - public static final int NORMAL_COOLDOWN = 20 * 40; + public static final int NORMAL_DURATION = 10; + public static final int NORMAL_COOLDOWN = 20 * 45; - public static final int NETHERITE_DURATION = 20; - public static final int NETHERITE_COOLDOWN = 20 * 30; + public static final int NETHERITE_DURATION = 15; + public static final int NETHERITE_COOLDOWN = 20 * 40; - public static final int RESIN_DURATION = 10; + public static final int RESIN_DURATION = 5; public static final int RESIN_COOLDOWN = 20 * 15; public RaiserAbility() { diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/RibAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/RibAbility.java index 7ea7789..d013cc6 100755 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/RibAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/RibAbility.java @@ -66,7 +66,7 @@ public class RibAbility extends AbstractAbility { !shaper.activeShellTasks.containsKey(liv.getUniqueId()) && main.man().abilityBackend.abilityAllowed(caster, liv.getLocation()), target->{ - target.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS,20*20,1,true,false,false)); + target.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS,20*6,1,true,false,false)); target.setFireTicks(20*20); }); }); @@ -162,8 +162,7 @@ public class RibAbility extends AbstractAbility { damageDealt = true; target.damage(spikeDamage, caster); - Vector knockDir = new Vector(random.nextGaussian() * 0.15, 0.9 + random.nextDouble()*0.2, random.nextGaussian() * 0.15); - target.setVelocity(target.getVelocity().add(knockDir)); + target.setVelocity(target.getVelocity().add(new Vector(0,0.5,0))); new SoundPlayer(target.getLocation(), Sound.ENTITY_PLAYER_HURT_ON_FIRE, 1.0f, 1.0f).playWithin(15); world.spawnParticle(Particle.LAVA, target.getEyeLocation(), 8, 0.2, 0.2, 0.2, 0); world.spawnParticle(Particle.ASH, target.getLocation(), 20, 0.5,0.5,0.5,0); diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SentryAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SentryAbility.java index 9ffe6a2..530bb0b 100755 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SentryAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SentryAbility.java @@ -10,6 +10,7 @@ import me.trouper.trimserver.utils.text.Text; import me.trouper.trimserver.utils.visual.BlockDisplayRaytracer; import me.trouper.trimserver.utils.visual.CustomDisplayRaytracer; import org.bukkit.*; +import org.bukkit.attribute.Attribute; import org.bukkit.block.data.BlockData; import org.bukkit.damage.DamageSource; import org.bukkit.damage.DamageType; @@ -76,7 +77,9 @@ public class SentryAbility extends AbstractAbility { Shulker dummy = w.spawn(turret.getLocation().clone().subtract(0,3,0),Shulker.class,shulker->{ shulker.setInvisible(true); - shulker.setHealth(10); + shulker.registerAttribute(Attribute.MAX_HEALTH); + shulker.getAttribute(Attribute.MAX_HEALTH).setBaseValue(40); + shulker.setHealth(40); shulker.customName(Text.color("%s's Sentry\n".formatted(owner.getName()))); shulker.setAI(false); shulker.addScoreboardTag("$/TrimServer/ Temp"); @@ -95,7 +98,7 @@ public class SentryAbility extends AbstractAbility { legsMat, start, end.toVector().subtract(start.toVector()), - 0.1, // leg thickness + 0.1, start.distance(end), 20 * secondsAlive + 1 )); @@ -129,12 +132,13 @@ public class SentryAbility extends AbstractAbility { String bar = Text.generateProgressBar(10, maxAmmo, chamber); meter.text(Text.color("%s's Sentry\n".formatted(owner.getName()) + "Ammo " + bar)); - Optional target = TargetingUtils.getClosestLivingEntity(finalLoc,15,p -> !p.isDead() && + Optional target = TargetingUtils.getClosestLivingEntity(finalLoc,15,liv -> + !liv.isDead() && (liv instanceof Player p && !p.equals(owner) && !main.man().trustBackend.trusts(owner,p) && !shaper.activeShellTasks.containsKey(p.getUniqueId()) && - PlayerUtils.combatAllowed(p,owner) && - !p.equals(dummy) + PlayerUtils.combatAllowed(p,owner)) || + (liv instanceof Enemy m && !m.equals(dummy)) ); if (target.isPresent()) { @@ -145,14 +149,15 @@ public class SentryAbility extends AbstractAbility { .subtract(turret.getLocation().toVector()); Vector dir = toEye.clone().normalize(); - float yaw = (float)(Math.toDegrees(Math.atan2(dir.getZ(), dir.getX())) - 90 + 180); + float yaw = (float)(Math.toDegrees(Math.atan2(dir.getZ(), dir.getX())) - 90 + 180); float pitch = (float)(Math.toDegrees(Math.asin(dir.getY()))); turret.setRotation(yaw, pitch); + w.spawnParticle(Particle.FLASH, turret.getLocation(), 1, 0,0,0, 0); CustomDisplayRaytracer.trace(turret.getLocation(),dir,60,0.5,point -> { List hits = new ArrayList<>(w.getNearbyEntities(point.getLoc(), 0.5,0.5,0.5, entity -> { - return entity instanceof LivingEntity living && !(living instanceof Shulker) && !living.isDead() && living != owner; + return entity instanceof LivingEntity living && !(living.equals(dummy)) && !living.isDead() && living != owner; })); hits.forEach(t -> { if (t instanceof LivingEntity liv) { diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/ShaperAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/ShaperAbility.java index bfed231..cea3e11 100755 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/ShaperAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/ShaperAbility.java @@ -5,6 +5,7 @@ import me.trouper.trimserver.server.systems.abilities.PatternInfo; import me.trouper.trimserver.server.systems.abilities.AbstractAbility; import me.trouper.trimserver.utils.SoundPlayer; import me.trouper.trimserver.utils.TargetingUtils; +import me.trouper.trimserver.utils.text.CustomBossBar; import org.bukkit.*; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeModifier; @@ -58,6 +59,7 @@ public class ShaperAbility extends AbstractAbility implements Listener { UUID playerUUID = player.getUniqueId(); if (activeShellTasks.containsKey(playerUUID)) return; + CustomBossBar.showBossBar(main.getPlugin(),player,"Terra Shell",duration); World world = player.getWorld(); List shellParts = new ArrayList<>(); @@ -111,6 +113,7 @@ public class ShaperAbility extends AbstractAbility implements Listener { if (!player.isOnline() || ticksElapsed >= finalDuration) { shatterShell(player, finalShatterDamage, finalShatterRadius, blockMaterialForShell); cleanupShellProperties(playerUUID, player); + CustomBossBar.removeBossBar(player); cancel(); return; } diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SilenceAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SilenceAbility.java index 329fc4d..1a2b2ee 100755 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SilenceAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SilenceAbility.java @@ -27,15 +27,15 @@ import java.util.Optional; @PatternInfo(name = "Warden's Call", description = "Now I am become warden, destroyer of ears.") public class SilenceAbility extends AbstractAbility { - - public static final double NORMAL_DAMAGE = 10; + // Damage is true once again + public static final double NORMAL_DAMAGE = 7; public static final int NORMAL_COOLDOWN = 20 * 25; - public static final double NETHERITE_DAMAGE = 30; - public static final int NETHERITE_COOLDOWN = 20 * 12; + public static final double NETHERITE_DAMAGE = 10; + public static final int NETHERITE_COOLDOWN = 20 * 22; - public static final double RESIN_DAMAGE = 10; - public static final int RESIN_COOLDOWN = 20 * 6; + public static final double RESIN_DAMAGE = 5; + public static final int RESIN_COOLDOWN = 20 * 15; public SilenceAbility() { super(TrimPattern.SILENCE); @@ -71,7 +71,7 @@ public class SilenceAbility extends AbstractAbility { PlayerUtils.combatAllowed(entity,player) ); targets.stream().filter(t -> t instanceof LivingEntity).map(t-> (LivingEntity) t).forEach(target -> { - target.damage(damage,DamageSource.builder(DamageType.SONIC_BOOM).withDirectEntity(player).build()); + PlayerUtils.dealTrueDamage(target,DamageSource.builder(DamageType.SONIC_BOOM).withDirectEntity(player).build(),damage); }); return !targets.isEmpty(); },(point,blockHit) -> { @@ -84,77 +84,77 @@ public class SilenceAbility extends AbstractAbility { },30); } - @MaterialInfo(name = "Amethyst ", description = "Shoot a sonic blast like the warden. Deals 15 true damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Amethyst ", description = "Shoot a sonic blast like the warden. Deals 7 true damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean amethystAbility(Player player) { shootSonicBoom(player, NORMAL_DAMAGE); return true; } - @MaterialInfo(name = "Copper ", description = "Shoot a sonic blast like the warden. Deals 15 true damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Copper ", description = "Shoot a sonic blast like the warden. Deals 7 true damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean copperAbility(Player player) { shootSonicBoom(player, NORMAL_DAMAGE); return true; } - @MaterialInfo(name = "Diamond ", description = "Shoot a sonic blast like the warden. Deals 15 true damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Diamond ", description = "Shoot a sonic blast like the warden. Deals 7 true damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean diamondAbility(Player player) { shootSonicBoom(player, NORMAL_DAMAGE); return true; } - @MaterialInfo(name = "Emerald Bolt", description = "Shoot a sonic blast like the warden. Deals 15 true damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Emerald Bolt", description = "Shoot a sonic blast like the warden. Deals 7 true damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean emeraldAbility(Player player) { shootSonicBoom(player, NORMAL_DAMAGE); return true; } - @MaterialInfo(name = "Gold ", description = "Shoot a sonic blast like the warden. Deals 15 true damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Gold ", description = "Shoot a sonic blast like the warden. Deals 7 true damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean goldAbility(Player player) { shootSonicBoom(player, NORMAL_DAMAGE); return true; } - @MaterialInfo(name = "Iron ", description = "Shoot a sonic blast like the warden. Deals 15 true damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Iron ", description = "Shoot a sonic blast like the warden. Deals 7 true damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean ironAbility(Player player) { shootSonicBoom(player, NORMAL_DAMAGE); return true; } - @MaterialInfo(name = "Lapis ", description = "Shoot a sonic blast like the warden. Deals 15 true damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Lapis ", description = "Shoot a sonic blast like the warden. Deals 7 true damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean lapisAbility(Player player) { shootSonicBoom(player, NORMAL_DAMAGE); return true; } - @MaterialInfo(name = "Netherite ", description = "Shoot a sonic blast like the warden. Deals 15 true damage", cooldownTicks = NETHERITE_COOLDOWN) + @MaterialInfo(name = "Netherite ", description = "Shoot a sonic blast like the warden. Deals 10 true damage", cooldownTicks = NETHERITE_COOLDOWN) @Override public boolean netheriteAbility(Player player) { shootSonicBoom(player, NETHERITE_DAMAGE); return true; } - @MaterialInfo(name = "Quartz ", description = "Shoot a sonic blast like the warden. Deals 15 true damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Quartz ", description = "Shoot a sonic blast like the warden. Deals 7 true damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean quartzAbility(Player player) { shootSonicBoom(player, NORMAL_DAMAGE); return true; } - @MaterialInfo(name = "Redstone ", description = "Shoot a sonic blast like the warden. Deals 15 true damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Redstone ", description = "Shoot a sonic blast like the warden. Deals 7 true damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean redstoneAbility(Player player) { shootSonicBoom(player, NORMAL_DAMAGE); return true; } - @MaterialInfo(name = "Resin ", description = "Shoot a sonic blast like the warden. Deals 15 true damage", cooldownTicks = RESIN_COOLDOWN) + @MaterialInfo(name = "Resin ", description = "Shoot a sonic blast like the warden. Deals 4 true damage", cooldownTicks = RESIN_COOLDOWN) @Override public boolean resinAbility(Player player) { shootSonicBoom(player, RESIN_DAMAGE); diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SnoutAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SnoutAbility.java index 8125063..f629412 100755 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SnoutAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SnoutAbility.java @@ -8,11 +8,15 @@ import me.trouper.trimserver.utils.Verbose; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.Sound; +import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.*; import org.bukkit.event.EventHandler; import org.bukkit.event.entity.EntityTargetLivingEntityEvent; import org.bukkit.event.entity.EntityTransformEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.trim.TrimPattern; import org.bukkit.persistence.PersistentDataType; import org.bukkit.scoreboard.Scoreboard; @@ -57,9 +61,13 @@ public class SnoutAbility extends AbstractAbility { team = board.registerNewTeam("glow_" + color.asHexString()); team.color(color); } - + ItemStack weapon = new ItemStack(Material.NETHERITE_AXE); + ItemMeta meta = weapon.getItemMeta(); + meta.addEnchant(Enchantment.SHARPNESS,3,false); team.addEntity(brute); brute.setGlowing(true); + brute.getEquipment().setItemInMainHand(weapon); + brute.getEquipment().setItemInMainHandDropChance(0F); brute.customName(Text.color(owner.getName() + "'s Piglin Brute").color(color)); brute.setCustomNameVisible(true); diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/TideAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/TideAbility.java index 930b9dd..63cb964 100755 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/TideAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/TideAbility.java @@ -20,6 +20,7 @@ import org.bukkit.util.Vector; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; @PatternInfo(name = "Tidal Wave", description = "No lifeguard on duty, swim at your own risk!") public class TideAbility extends AbstractAbility { @@ -44,9 +45,18 @@ public class TideAbility extends AbstractAbility { public void spawnTidalWave(Player caster, double radius, double damage) { AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER); ShaperAbility shaper = (ShaperAbility) shaperInstance; - - new SoundPlayer(caster.getLocation(), Sound.WEATHER_RAIN_ABOVE, 1, 2F).playWithin(30); + new SoundPlayer(caster.getLocation(), Sound.ENTITY_PLAYER_SPLASH_HIGH_SPEED, 1, 1F).playWithin(30); + + AtomicInteger i = new AtomicInteger(); + Bukkit.getScheduler().runTaskTimer(main.getPlugin(),(task)->{ + if (i.getAndIncrement() >= 8) { + task.cancel(); + return; + } + new SoundPlayer(caster.getLocation(), Sound.WEATHER_RAIN_ABOVE, 1, 2F).playWithin(30); + new SoundPlayer(caster.getLocation(), Sound.ENTITY_GUARDIAN_ATTACK, 1, 2F).playWithin(30); + },1,10); Vector direction = caster.getLocation().getDirection(); DisplayUtils.waveFan(caster.getLocation().add(0, 2,0),radius,direction,90,0.1, point->{ @@ -65,6 +75,7 @@ public class TideAbility extends AbstractAbility { target.damage(damage, DamageSource.builder(DamageType.DROWN).withDirectEntity(caster).build()); blockSound.playWithin(10); caster.getWorld().spawnParticle(Particle.FALLING_WATER,target.getLocation().clone().add(0,1,0),10,0.2,1,0.2,0.1); + new SoundPlayer(target.getLocation(), Sound.ENTITY_DROWNED_HURT_WATER, 0.9f, 1.4f).playWithin(15); }); },0.2); Bukkit.getScheduler().runTaskLater(main.getPlugin(),()->{ @@ -124,7 +135,7 @@ public class TideAbility extends AbstractAbility { return true; } - @MaterialInfo(name = "Netherite ", description = "Summon a tidal wave in the direction you are looking. Pushes away enemies, deals 6 damage", cooldownTicks = NETHERITE_COOLDOWN) + @MaterialInfo(name = "Netherite ", description = "Summon a tidal wave in the direction you are looking. Pushes away enemies, deals 15 damage", cooldownTicks = NETHERITE_COOLDOWN) @Override public boolean netheriteAbility(Player player) { spawnTidalWave(player,NETHERITE_RADIUS,NETHERITE_DAMAGE); @@ -145,7 +156,7 @@ public class TideAbility extends AbstractAbility { return true; } - @MaterialInfo(name = "Resin ", description = "Summon a tidal wave in the direction you are looking. Pushes away enemies, deals 6 damage", cooldownTicks = RESIN_COOLDOWN) + @MaterialInfo(name = "Resin ", description = "Summon a tidal wave in the direction you are looking. Pushes away enemies, deals 3 damage", cooldownTicks = RESIN_COOLDOWN) @Override public boolean resinAbility(Player player) { spawnTidalWave(player,RESIN_RADIUS,RESIN_DAMAGE); diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/VexAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/VexAbility.java index a1e234c..bb66251 100755 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/VexAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/VexAbility.java @@ -8,10 +8,14 @@ import me.trouper.trimserver.utils.Verbose; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.Sound; +import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.*; import org.bukkit.event.EventHandler; import org.bukkit.event.entity.EntityTargetLivingEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.trim.TrimPattern; import org.bukkit.persistence.PersistentDataType; import org.bukkit.scoreboard.Scoreboard; @@ -50,8 +54,15 @@ public class VexAbility extends AbstractAbility { team.color(color); } + ItemStack weapon = new ItemStack(Material.NETHERITE_SWORD); + ItemMeta meta = weapon.getItemMeta(); + meta.addEnchant(Enchantment.SHARPNESS,3,false); + weapon.setItemMeta(meta); team.addEntity(vex); vex.setGlowing(true); + vex.getEquipment().setItemInMainHand(weapon); + vex.getEquipment().setItemInMainHandDropChance(0F); + vex.setLeftHanded(false); vex.customName(Text.color(owner.getName() + "'s Vex").color(color)); vex.setCustomNameVisible(true); diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/WardAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/WardAbility.java index a2e6001..0108fd7 100755 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/WardAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/WardAbility.java @@ -6,6 +6,7 @@ import me.trouper.trimserver.server.systems.abilities.MaterialInfo; import me.trouper.trimserver.server.systems.abilities.AbstractAbility; import me.trouper.trimserver.server.systems.abilities.PatternInfo; import me.trouper.trimserver.utils.SoundPlayer; +import me.trouper.trimserver.utils.text.CustomBossBar; import me.trouper.trimserver.utils.text.Text; import me.trouper.trimserver.utils.Verbose; import org.bukkit.*; @@ -52,7 +53,7 @@ public class WardAbility extends AbstractAbility { private void transformPlayer(Player player) { Verbose.send( "transformPlayer called for %s; currently disguised? %b", player.getName(), activeDisguises.containsKey(player.getUniqueId())); - + CustomBossBar.showBossBar(main.getPlugin(),player,"Warden Disguise",30 * 20); if (activeDisguises.containsKey(player.getUniqueId())) { Verbose.send( "Player %s already disguised; removing disguise", player.getName()); removeDisguise(player); @@ -109,6 +110,7 @@ public class WardAbility extends AbstractAbility { Verbose.send( "No disguise data found for %s; aborting", player.getName()); return; } + CustomBossBar.removeBossBar(player); data.disguise.remove(); Verbose.send( "Removed disguise entity %s", data.disguise.getUniqueId()); diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/WayfinderAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/WayfinderAbility.java index 59896d7..7bd8266 100755 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/WayfinderAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/WayfinderAbility.java @@ -8,6 +8,7 @@ import me.trouper.trimserver.utils.SoundPlayer; import me.trouper.trimserver.utils.TargetingUtils; import me.trouper.trimserver.utils.visual.DisplayUtils; import org.bukkit.Bukkit; +import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.Sound; import org.bukkit.block.Block; @@ -16,11 +17,12 @@ import org.bukkit.block.data.BlockData; import org.bukkit.damage.DamageSource; import org.bukkit.damage.DamageType; import org.bukkit.entity.*; +import org.bukkit.event.EventHandler; +import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.inventory.meta.trim.TrimPattern; import org.bukkit.util.Vector; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import java.util.concurrent.atomic.AtomicInteger; @PatternInfo(name = "Big Step", description = "\"He boot too big for he gotdamn feet.\"") @@ -38,133 +40,148 @@ public class WayfinderAbility extends AbstractAbility { public static final double RESIN_DAMAGE = 26; public static final double RESIN_RADIUS = 4; + private final Set awaitStomp = new HashSet<>(); + public WayfinderAbility() { super(TrimPattern.WAYFINDER); } public void stomp(Player caster, double damage, double radius) { if (caster.getLocation().clone().subtract(0,1,0).getBlock().isPassable()) return; - - AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER); - ShaperAbility shaper = (ShaperAbility) shaperInstance; + awaitStomp.add(caster.getUniqueId()); caster.setVelocity(caster.getVelocity().setY(caster.getVelocity().getY() + 1.5)); SoundPlayer jump = new SoundPlayer(caster.getLocation(), Sound.ENTITY_ENDER_DRAGON_FLAP, 1, 2); jump.playWithin(10); - DisplayUtils.disc(caster.getLocation(),3,0.5,0.25,point->{ + DisplayUtils.disc(caster.getLocation(),2,0.5,0.25,point->{ point.getWorld().spawnParticle(Particle.POOF,point,1,0,0,0,0); }); AtomicInteger expiry = new AtomicInteger(80); Bukkit.getScheduler().runTaskTimer(main.getPlugin(),task -> { - if (expiry.getAndDecrement() <= 0) { + if (expiry.getAndDecrement() <= 0 || !awaitStomp.contains(caster.getUniqueId())) { + awaitStomp.remove(caster.getUniqueId()); task.cancel(); return; } if (!caster.getLocation().clone().subtract(0,1,0).getBlock().isPassable()) { - - DisplayUtils.wave(caster.getLocation(),10,1,1,point -> { - Block thrown = point.getWorld().getHighestBlockAt((int) point.x(), (int) point.z()); - BlockData data = thrown.getBlockData(); - BlockState state = point.getWorld().getHighestBlockAt((int) point.x(), (int) point.z()).getState(); - - FallingBlock block = (FallingBlock) point.getWorld().spawnEntity(point.clone(), EntityType.FALLING_BLOCK); - block.setBlockData(data); - block.setBlockState(state); - block.setVelocity(new Vector(0,0.1,0)); - block.setCancelDrop(true); - }); - - TargetingUtils.areaAffect(caster.getLocation(),radius,target -> !target.isDead() && - !target.equals(caster) && - !main.man().trustBackend.trusts(caster,target) && - !shaper.activeShellTasks.containsKey(target.getUniqueId()) && - PlayerUtils.combatAllowed(target,caster), target->{ - SoundPlayer hit = new SoundPlayer(target.getLocation(), Sound.ENTITY_EVOKER_FANGS_ATTACK, 1, 2); - Vector direction = target.getLocation().toVector().subtract(caster.getEyeLocation().toVector()).normalize(); - target.setVelocity(direction.multiply(0.5).setY(0.3)); - target.damage(damage, DamageSource.builder(DamageType.FALLING_BLOCK).build()); - hit.playWithin(10); - caster.getWorld().spawnParticle(Particle.DAMAGE_INDICATOR,target.getLocation().clone().add(0,1,0),10,0.2,1,0.2,0.1); - }); - + land(caster,damage,radius); task.cancel(); } },10,2); } + + public void land(Player caster, double damage, double radius) { + AbstractAbility shaperInstance = main.man().abilityBackend.getAbility(TrimPattern.SHAPER); + ShaperAbility shaper = (ShaperAbility) shaperInstance; - @MaterialInfo(name = "Amethyst ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 5 Damage", cooldownTicks = NORMAL_COOLDOWN) + DisplayUtils.wave(caster.getLocation(),10,1,1,point -> { + Block thrown = point.getWorld().getHighestBlockAt((int) point.x(), (int) point.z()); + BlockData data = thrown.getBlockData(); + BlockState state = point.getWorld().getHighestBlockAt((int) point.x(), (int) point.z()).getState(); + + FallingBlock block = (FallingBlock) point.getWorld().spawnEntity(point.clone(), EntityType.FALLING_BLOCK); + block.setBlockData(data); + block.setBlockState(state); + block.setVelocity(new Vector(0,0.1,0)); + block.setCancelDrop(true); + }); + + TargetingUtils.areaAffect(caster.getLocation(),radius,target -> !target.isDead() && + !target.equals(caster) && + !main.man().trustBackend.trusts(caster,target) && + !shaper.activeShellTasks.containsKey(target.getUniqueId()) && + PlayerUtils.combatAllowed(target,caster), target->{ + SoundPlayer hit = new SoundPlayer(target.getLocation(), Sound.ENTITY_EVOKER_FANGS_ATTACK, 1, 2); + Vector direction = target.getLocation().toVector().subtract(caster.getEyeLocation().toVector()).normalize(); + target.setVelocity(direction.multiply(0.5).setY(0.3)); + target.damage(damage, DamageSource.builder(DamageType.FALLING_BLOCK).build()); + hit.playWithin(10); + caster.getWorld().spawnParticle(Particle.DAMAGE_INDICATOR,target.getLocation().clone().add(0,1,0),10,0.2,1,0.2,0.1); + caster.getWorld().spawnParticle(Particle.BLOCK_CRUMBLE,target.getLocation().clone().add(0,1,0),20,0.2,1,0.2,0.1, target.getLocation().clone().subtract(0,2,0).getBlock().getBlockData()); + }); + } + + @EventHandler + public void onDamage(EntityDamageEvent e) { + if (e.getCause().equals(EntityDamageEvent.DamageCause.FALL) && awaitStomp.remove(e.getEntity().getUniqueId())) { + e.setCancelled(true); + e.getEntity().getWorld().playSound(e.getEntity(),Sound.ENTITY_ZOMBIE_BREAK_WOODEN_DOOR,1,1); + } + } + + @MaterialInfo(name = "Amethyst ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 36 damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean amethystAbility(Player player) { stomp(player,NORMAL_DAMAGE,NORMAL_RADIUS); return true; } - @MaterialInfo(name = "Copper ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 5 Damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Copper ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 36 damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean copperAbility(Player player) { stomp(player,NORMAL_DAMAGE,NORMAL_RADIUS); return true; } - @MaterialInfo(name = "Diamond ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 5 Damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Diamond ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 36 damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean diamondAbility(Player player) { stomp(player,NORMAL_DAMAGE,NORMAL_RADIUS); return true; } - @MaterialInfo(name = "Emerald Bolt", description = "Jump into the air, creating a seismic disturbance when you land. Deals 5 Damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Emerald Bolt", description = "Jump into the air, creating a seismic disturbance when you land. Deals 36 damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean emeraldAbility(Player player) { stomp(player,NORMAL_DAMAGE,NORMAL_RADIUS); return true; } - @MaterialInfo(name = "Gold ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 5 Damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Gold ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 36 damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean goldAbility(Player player) { stomp(player,NORMAL_DAMAGE,NORMAL_RADIUS); return true; } - @MaterialInfo(name = "Iron ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 5 Damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Iron ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 36 damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean ironAbility(Player player) { stomp(player,NORMAL_DAMAGE,NORMAL_RADIUS); return true; } - @MaterialInfo(name = "Lapis ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 5 Damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Lapis ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 36 damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean lapisAbility(Player player) { stomp(player,NORMAL_DAMAGE,NORMAL_RADIUS); return true; } - @MaterialInfo(name = "Netherite ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 5 Damage", cooldownTicks = NETHERITE_COOLDOWN) + @MaterialInfo(name = "Netherite ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 46 damage", cooldownTicks = NETHERITE_COOLDOWN) @Override public boolean netheriteAbility(Player player) { stomp(player,NETHERITE_DAMAGE,NETHERITE_RADIUS); return true; } - @MaterialInfo(name = "Quartz ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 5 Damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Quartz ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 36 damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean quartzAbility(Player player) { stomp(player,NORMAL_DAMAGE,NORMAL_RADIUS); return true; } - @MaterialInfo(name = "Redstone ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 5 Damage", cooldownTicks = NORMAL_COOLDOWN) + @MaterialInfo(name = "Redstone ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 36 damage", cooldownTicks = NORMAL_COOLDOWN) @Override public boolean redstoneAbility(Player player) { stomp(player,NORMAL_DAMAGE,NORMAL_RADIUS); return true; } - @MaterialInfo(name = "Resin ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 5 Damage", cooldownTicks = RESIN_COOLDOWN) + @MaterialInfo(name = "Resin ", description = "Jump into the air, creating a seismic disturbance when you land. Deals 26 damage", cooldownTicks = RESIN_COOLDOWN) @Override public boolean resinAbility(Player player) { stomp(player,RESIN_DAMAGE,RESIN_RADIUS); diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/WildAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/WildAbility.java index 6900907..9c0ef0a 100755 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/WildAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/WildAbility.java @@ -50,7 +50,7 @@ public class WildAbility extends AbstractAbility { caster.getWorld().playSound(casterStartLoc, Sound.ENTITY_FISHING_BOBBER_THROW, 1.0f, 0.8f); - target.addPotionEffect(new PotionEffect(PotionEffectType.POISON,4*20,1,true,false,false)); + target.addPotionEffect(new PotionEffect(PotionEffectType.POISON,6*20,1,true,false,false)); final List currentVineSegment = new ArrayList<>(1); diff --git a/src/main/java/me/trouper/trimserver/utils/PlayerUtils.java b/src/main/java/me/trouper/trimserver/utils/PlayerUtils.java index b31003b..ff97f66 100755 --- a/src/main/java/me/trouper/trimserver/utils/PlayerUtils.java +++ b/src/main/java/me/trouper/trimserver/utils/PlayerUtils.java @@ -13,6 +13,9 @@ import org.bukkit.damage.DamageSource; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; public class PlayerUtils implements Main { @@ -87,28 +90,30 @@ public class PlayerUtils implements Main { public static void dealTrueDamage(LivingEntity target, DamageSource source, double amount) { if (source.getDirectEntity() instanceof Player a && target instanceof Player t && !combatAllowed(t,a)) return; - - double newHealth = target.getHealth() - amount; - if (newHealth <= 0) { - target.setHealth(1); - target.damage(30, source); - } else { - target.setHealth(newHealth); - target.damage(1, source); + + EntityEquipment equipment = target.getEquipment(); + if (equipment == null) { + target.damage(amount,source); + return; } + + ItemStack helmet = equipment.getHelmet(); + ItemStack chestplate = equipment.getChestplate(); + ItemStack leggings = equipment.getLeggings(); + ItemStack boots = equipment.getBoots(); - Entity attacker = source.getDirectEntity(); - if (attacker instanceof LivingEntity) { - double dx = target.getX() - attacker.getX(); - double dz = target.getZ() - attacker.getZ(); - double magnitude = Math.sqrt(dx * dx + dz * dz); - - if (magnitude > 0) { - double strength = 0.4; - dx /= magnitude; - dz /= magnitude; - target.setVelocity(target.getVelocity().add(new Vector(dx * strength, 0.1, dz * strength))); - } + equipment.setHelmet(null); + equipment.setChestplate(null); + equipment.setLeggings(null); + equipment.setBoots(null); + + try { + target.damage(amount,source); + } finally { + equipment.setHelmet(helmet); + equipment.setChestplate(chestplate); + equipment.setLeggings(leggings); + equipment.setBoots(boots); } } diff --git a/src/main/java/me/trouper/trimserver/utils/TargetingUtils.java b/src/main/java/me/trouper/trimserver/utils/TargetingUtils.java index d91fe3b..9989218 100755 --- a/src/main/java/me/trouper/trimserver/utils/TargetingUtils.java +++ b/src/main/java/me/trouper/trimserver/utils/TargetingUtils.java @@ -225,8 +225,8 @@ public class TargetingUtils { * @return An {@link Optional} containing the {@link LivingEntity} closest to the aim vector, * or an empty Optional if no suitable entity is found or world is null. */ - public static Optional getLivingEntityClosestToVector(Location originEyeLocation, Vector direction, double maxDistance, double maxAngleRadians) { - return getLivingEntityClosestToVector(originEyeLocation, direction, maxDistance, maxAngleRadians, entity -> true); + public static Optional livingClosestAngle(Location originEyeLocation, Vector direction, double maxDistance, double maxAngleRadians) { + return livingClosestAngle(originEyeLocation, direction, maxDistance, maxAngleRadians, entity -> true); } /** @@ -242,7 +242,7 @@ public class TargetingUtils { * @return An {@link Optional} containing the {@link LivingEntity} closest to the aim vector and matching the filter, * or an empty Optional if no suitable entity is found or world is null. */ - public static Optional getLivingEntityClosestToVector(Location originEyeLocation, Vector direction, double maxDistance, double maxAngleRadians, Predicate filter) { + public static Optional livingClosestAngle(Location originEyeLocation, Vector direction, double maxDistance, double maxAngleRadians, Predicate filter) { if (originEyeLocation == null || originEyeLocation.getWorld() == null || direction == null || filter == null) { return Optional.empty(); } diff --git a/src/main/java/me/trouper/trimserver/utils/text/CustomBossBar.java b/src/main/java/me/trouper/trimserver/utils/text/CustomBossBar.java index 047943e..70029e6 100644 --- a/src/main/java/me/trouper/trimserver/utils/text/CustomBossBar.java +++ b/src/main/java/me/trouper/trimserver/utils/text/CustomBossBar.java @@ -19,7 +19,6 @@ public class CustomBossBar { private static final Map activeBossBars = new ConcurrentHashMap<>(); - // Private constructor to prevent instantiation private CustomBossBar() {} /** @@ -33,20 +32,15 @@ public class CustomBossBar { public static void showBossBar(Plugin plugin, Player player, Component text, BossBarConfig config) { UUID playerId = player.getUniqueId(); - // Remove existing boss bar if present removeBossBar(player); - // Create new boss bar BossBar bossBar = BossBar.bossBar(text, 1.0f, config.color, config.overlay); - // Show to player player.showBossBar(bossBar); - // Create and store boss bar data PlayerBossBarData data = new PlayerBossBarData(bossBar, config); activeBossBars.put(playerId, data); - // Start countdown if duration is specified if (config.durationTicks > 0) { startCountdown(plugin, player, data); } @@ -137,12 +131,10 @@ public class CustomBossBar { PlayerBossBarData data = activeBossBars.remove(playerId); if (data != null) { - // Cancel countdown task if running if (data.countdownTask != null && !data.countdownTask.isCancelled()) { data.countdownTask.cancel(); } - // Hide boss bar player.hideBossBar(data.bossBar); } } @@ -193,7 +185,7 @@ public class CustomBossBar { } /** - * Removes all active boss bars (useful for plugin disable) + * Removes all active boss bars */ public static void removeAllBossBars() { for (Player player : Bukkit.getOnlinePlayers()) { @@ -241,11 +233,9 @@ public class CustomBossBar { data.remainingTicks--; - // Update progress bar based on remaining time float progress = (float) data.remainingTicks / data.config.durationTicks; data.bossBar.progress(Math.max(0.0f, progress)); - // Change color based on remaining time if configured if (data.config.changeColorOnLowTime) { if (progress <= 0.2f) { data.bossBar.color(BossBar.Color.RED); @@ -254,17 +244,13 @@ public class CustomBossBar { } } - // Remove when time is up if (data.remainingTicks <= 0) { removeBossBar(player); } } }.runTaskTimer(plugin, 0L, 1L); } - - /** - * Internal class to hold boss bar data for each player - */ + private static class PlayerBossBarData { final BossBar bossBar; final BossBarConfig config; diff --git a/src/main/java/me/trouper/trimserver/utils/text/Text.java b/src/main/java/me/trouper/trimserver/utils/text/Text.java index a21f7f0..333d50c 100755 --- a/src/main/java/me/trouper/trimserver/utils/text/Text.java +++ b/src/main/java/me/trouper/trimserver/utils/text/Text.java @@ -136,7 +136,9 @@ public class Text implements Main { int currentLineLength = offset; for (String word : words) { - if (currentLineLength + word.length() + 1 > maxLineLength) { + int wordVisibleLength = getVisibleLength(word); + + if (currentLineLength + wordVisibleLength + 1 > maxLineLength) { lines.add(currentLine.toString()); currentLine = new StringBuilder(); currentLineLength = 0; @@ -148,7 +150,7 @@ public class Text implements Main { } currentLine.append(word); - currentLineLength += word.length(); + currentLineLength += wordVisibleLength; } if (!currentLine.isEmpty()) { @@ -158,6 +160,10 @@ public class Text implements Main { return lines; } + private static int getVisibleLength(String text) { + return text.replaceAll("&(?:[0-9a-fk-or]|#[0-9a-fA-F]{6})", "").length(); + } + public static String getActiveFormatting(String text) { final Pattern pattern = Pattern.compile("&[0-9a-fk-or]"); final Matcher matcher = pattern.matcher(text); @@ -198,35 +204,40 @@ public class Text implements Main { public enum Pallet { ERROR( - NamedTextColor.RED, - NamedTextColor.YELLOW, - NamedTextColor.GOLD, - NamedTextColor.DARK_RED, - new SoundData(Sound.BLOCK_NOTE_BLOCK_BASS,1)), + TextColor.color(0xFFB3BA), // Pastel red + TextColor.color(0xFFD6A5), // Pastel orange + TextColor.color(0xFFDFBA), // Peach + TextColor.color(0xFF9999), // Soft coral + new SoundData(Sound.BLOCK_NOTE_BLOCK_BASS, 1) + ), WARNING( - NamedTextColor.YELLOW, - NamedTextColor.GOLD, - NamedTextColor.RED, - NamedTextColor.DARK_RED, - new SoundData(Sound.BLOCK_NOTE_BLOCK_BIT,0.5F)), + TextColor.color(0xFFEAA7), // Light pastel yellow-orange + TextColor.color(0xFFFACD), // Lemon chiffon + TextColor.color(0xFFDAC1), // Light orange-pink + TextColor.color(0xFFD1DC), // Light pink + new SoundData(Sound.BLOCK_NOTE_BLOCK_BIT, 0.5F) + ), INFO( - NamedTextColor.GRAY, - NamedTextColor.WHITE, - NamedTextColor.AQUA, - NamedTextColor.DARK_AQUA, - new SoundData(Sound.BLOCK_NOTE_BLOCK_BELL,1)), + TextColor.color(0xAAAAFF), // Lavender blue + TextColor.color(0xDDDDFF), // Very light lavender + TextColor.color(0xB4E7FB), // Baby blue + TextColor.color(0xA298FF), // Pastel indigo + new SoundData(Sound.BLOCK_NOTE_BLOCK_BELL, 1) + ), SUCCESS( - NamedTextColor.GREEN, - NamedTextColor.DARK_GREEN, - NamedTextColor.YELLOW, - NamedTextColor.GOLD, - new SoundData(Sound.BLOCK_NOTE_BLOCK_CHIME,1)), + TextColor.color(0xAAFFAA), // Mint green + TextColor.color(0xC1F7C1), // Pale green + TextColor.color(0xFFFFAA), // Pale yellow + TextColor.color(0xFFF5C3), // Light creamy yellow + new SoundData(Sound.BLOCK_NOTE_BLOCK_CHIME, 1) + ), NEUTRAL( - NamedTextColor.GRAY, - NamedTextColor.WHITE, - NamedTextColor.DARK_AQUA, - NamedTextColor.BLUE, - new SoundData(Sound.BLOCK_NOTE_BLOCK_BELL,1)); + TextColor.color(0xCCCCCC), // Soft gray + TextColor.color(0xFFFFFF), // White + TextColor.color(0xC2E0F4), // Powder blue + TextColor.color(0xAAAAFF), // Lavender blue + new SoundData(Sound.BLOCK_NOTE_BLOCK_BELL, 1) + ); private final TextColor mainText; private final TextColor argDefault; @@ -242,7 +253,8 @@ public class Text implements Main { this.sound = sound; } } - + + public record SoundData(Sound sound, float pitch){}; public static String generateProgressBar(int length, int max, int current) { diff --git a/src/main/java/me/trouper/trimserver/utils/visual/DisplayEntityUtils.java b/src/main/java/me/trouper/trimserver/utils/visual/DisplayEntityUtils.java deleted file mode 100755 index 26ab6c5..0000000 --- a/src/main/java/me/trouper/trimserver/utils/visual/DisplayEntityUtils.java +++ /dev/null @@ -1,1706 +0,0 @@ -package me.trouper.trimserver.utils.visual; - -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.entity.*; -import org.bukkit.plugin.Plugin; -import org.bukkit.scheduler.BukkitRunnable; -import org.bukkit.scheduler.BukkitTask; -import org.bukkit.util.Transformation; -import org.bukkit.util.Vector; -import org.joml.*; - -import java.lang.Math; -import java.util.*; -import java.util.Random; -import java.util.function.Consumer; - -/** - * A comprehensive utility class for working with Display Entities in the Bukkit API. - * This utility handles all the transformation math, allowing developers to focus on the visual aspects of their plugins. - */ -public final class DisplayEntityUtils { - private DisplayEntityUtils() { - // Private constructor to prevent instantiation - } - - /** - * Creates a text display with the specified text at the given location. - * - * @param location The location to spawn the text display - * @param text The text to display - * @return The created TextDisplay entity - */ - public static TextDisplay createTextDisplay(Location location, String text) { - World world = location.getWorld(); - TextDisplay display = (TextDisplay) world.spawnEntity(location, EntityType.TEXT_DISPLAY); - display.setText(text); - return display; - } - - /** - * Creates an item display with the specified item at the given location. - * - * @param location The location to spawn the item display - * @param item The item to display - * @return The created ItemDisplay entity - */ - public static ItemDisplay createItemDisplay(Location location, org.bukkit.inventory.ItemStack item) { - World world = location.getWorld(); - ItemDisplay display = (ItemDisplay) world.spawnEntity(location, EntityType.ITEM_DISPLAY); - display.setItemStack(item); - return display; - } - - /** - * Creates a block display with the specified block data at the given location. - * - * @param location The location to spawn the block display - * @param blockData The block data to display - * @return The created BlockDisplay entity - */ - public static BlockDisplay createBlockDisplay(Location location, org.bukkit.block.data.BlockData blockData) { - World world = location.getWorld(); - BlockDisplay display = (BlockDisplay) world.spawnEntity(location, EntityType.BLOCK_DISPLAY); - display.setBlock(blockData); - return display; - } - - /** - * Builder class for creating and configuring display entities with fluent API. - * - * @param The type of display entity - */ - public static class DisplayBuilder { - private final T display; - - private DisplayBuilder(T display) { - this.display = display; - } - - /** - * Starts building a text display. - * - * @param location The location to spawn the text display - * @param text The text to display - * @return A new DisplayBuilder for the text display - */ - public static DisplayBuilder text(Location location, String text) { - return new DisplayBuilder<>(createTextDisplay(location, text)); - } - - /** - * Starts building an item display. - * - * @param location The location to spawn the item display - * @param item The item to display - * @return A new DisplayBuilder for the item display - */ - public static DisplayBuilder item(Location location, org.bukkit.inventory.ItemStack item) { - return new DisplayBuilder<>(createItemDisplay(location, item)); - } - - /** - * Starts building a block display. - * - * @param location The location to spawn the block display - * @param blockData The block data to display - * @return A new DisplayBuilder for the block display - */ - public static DisplayBuilder block(Location location, org.bukkit.block.data.BlockData blockData) { - return new DisplayBuilder<>(createBlockDisplay(location, blockData)); - } - - /** - * Sets the display entity's transformation. - * - * @param transformation The transformation to apply - * @return This builder for chaining - */ - public DisplayBuilder transformation(Transformation transformation) { - display.setTransformation(transformation); - return this; - } - - /** - * Sets the display entity's size scale. - * - * @param scale The scale factor to apply uniformly in all dimensions - * @return This builder for chaining - */ - public DisplayBuilder scale(float scale) { - return scale(scale, scale, scale); - } - - /** - * Sets the display entity's size scale with different values for each dimension. - * - * @param x The X scale factor - * @param y The Y scale factor - * @param z The Z scale factor - * @return This builder for chaining - */ - public DisplayBuilder scale(float x, float y, float z) { - Transformation transform = display.getTransformation(); - Vector3f scale = new Vector3f(x, y, z); - display.setTransformation(new Transformation( - transform.getTranslation(), - transform.getLeftRotation(), - scale, - transform.getRightRotation() - )); - return this; - } - - /** - * Sets the display entity's translation. - * - * @param x The X translation - * @param y The Y translation - * @param z The Z translation - * @return This builder for chaining - */ - public DisplayBuilder translate(float x, float y, float z) { - Transformation transform = display.getTransformation(); - Vector3f translation = new Vector3f(x, y, z); - display.setTransformation(new Transformation( - translation, - transform.getLeftRotation(), - transform.getScale(), - transform.getRightRotation() - )); - return this; - } - - /** - * Sets the display entity's rotation using quaternions. - * - * @param quaternion The rotation quaternion - * @return This builder for chaining - */ - public DisplayBuilder rotate(Quaternionf quaternion) { - Transformation transform = display.getTransformation(); - display.setTransformation(new Transformation( - transform.getTranslation(), - quaternion, - transform.getScale(), - transform.getRightRotation() - )); - return this; - } - - /** - * Sets the display entity's rotation using Euler angles. - * - * @param pitch The pitch in degrees - * @param yaw The yaw in degrees - * @param roll The roll in degrees - * @return This builder for chaining - */ - public DisplayBuilder rotate(float pitch, float yaw, float roll) { - Quaternionf quaternion = EulerAngle.toQuaternion(Math.toRadians(pitch), Math.toRadians(yaw), Math.toRadians(roll)); - return rotate(quaternion); - } - - /** - * Sets the billboard mode for the display entity. - * - * @param mode The billboard mode - * @return This builder for chaining - */ - public DisplayBuilder billboard(Display.Billboard mode) { - display.setBillboard(mode); - return this; - } - - /** - * Sets the brightness for the display entity. - * - * @param blockLight The block light level - * @param skyLight The sky light level - * @return This builder for chaining - */ - public DisplayBuilder brightness(int blockLight, int skyLight) { - display.setBrightness(new Display.Brightness(blockLight, skyLight)); - return this; - } - - /** - * Sets the view range for the display entity. - * - * @param range The view range in blocks - * @return This builder for chaining - */ - public DisplayBuilder viewRange(float range) { - display.setViewRange(range); - return this; - } - - /** - * Sets the shadow radius for the display entity. - * - * @param radius The shadow radius - * @return This builder for chaining - */ - public DisplayBuilder shadowRadius(float radius) { - display.setShadowRadius(radius); - return this; - } - - /** - * Sets the shadow strength for the display entity. - * - * @param strength The shadow strength - * @return This builder for chaining - */ - public DisplayBuilder shadowStrength(float strength) { - display.setShadowStrength(strength); - return this; - } - - /** - * Configures the display entity if it's a TextDisplay. - * - * @param configurator A consumer that configures the TextDisplay - * @return This builder for chaining - */ - @SuppressWarnings("unchecked") - public DisplayBuilder textConfig(Consumer configurator) { - if (display instanceof TextDisplay) { - configurator.accept((TextDisplay) display); - } - return this; - } - - /** - * Configures the display entity if it's an ItemDisplay. - * - * @param configurator A consumer that configures the ItemDisplay - * @return This builder for chaining - */ - @SuppressWarnings("unchecked") - public DisplayBuilder itemConfig(Consumer configurator) { - if (display instanceof ItemDisplay) { - configurator.accept((ItemDisplay) display); - } - return this; - } - - /** - * Configures the display entity if it's a BlockDisplay. - * - * @param configurator A consumer that configures the BlockDisplay - * @return This builder for chaining - */ - @SuppressWarnings("unchecked") - public DisplayBuilder blockConfig(Consumer configurator) { - if (display instanceof BlockDisplay) { - configurator.accept((BlockDisplay) display); - } - return this; - } - - /** - * Gets the built display entity. - * - * @return The configured display entity - */ - public T build() { - return display; - } - } - - /** - * Utility class for working with Euler angles and quaternions. - */ - public static class EulerAngle { - /** - * Converts Euler angles to a quaternion. - * - * @param pitch The pitch in radians - * @param yaw The yaw in radians - * @param roll The roll in radians - * @return A quaternion representing the rotation - */ - public static Quaternionf toQuaternion(double pitch, double yaw, double roll) { - // Convert Euler angles to quaternion using JOML library - return new Quaternionf() - .rotateX((float) pitch) - .rotateY((float) yaw) - .rotateZ((float) roll); - } - - /** - * Converts a quaternion to Euler angles. - * - * @param quaternion The quaternion to convert - * @return An array containing [pitch, yaw, roll] in radians - */ - public static float[] toEulerAngles(Quaternionf quaternion) { - Vector3f euler = new Vector3f(); - quaternion.getEulerAnglesXYZ(euler); - return new float[]{euler.x, euler.y, euler.z}; - } - } - - /** - * Class for creating and managing animations for display entities. - */ - public static class Animator { - private final Plugin plugin; - private final Map animationTasks = new HashMap<>(); - - /** - * Creates a new Animator instance. - * - * @param plugin The plugin instance for scheduling tasks - */ - public Animator(Plugin plugin) { - this.plugin = plugin; - } - - /** - * Animates a display entity along a path. - * - * @param display The display entity to animate - * @param path The list of locations defining the path - * @param durationTicks The total duration of the animation in ticks - * @return A BukkitTask representing the animation task - */ - public BukkitTask animatePath(Display display, List path, long durationTicks) { - if (path.size() < 2) { - throw new IllegalArgumentException("Path must contain at least 2 points"); - } - - final int steps = path.size() - 1; - final long ticksPerStep = durationTicks / steps; - final UUID displayId = display.getUniqueId(); - - // Cancel any existing animation for this display - cancelAnimation(display); - - BukkitTask task = new BukkitRunnable() { - int currentStep = 0; - long currentTick = 0; - - @Override - public void run() { - if (currentStep >= steps || !display.isValid()) { - cancelAnimation(display); - return; - } - - Location start = path.get(currentStep); - Location end = path.get(currentStep + 1); - - // Calculate progress within current step (0.0 to 1.0) - float progress = (float) (currentTick % ticksPerStep) / ticksPerStep; - - // Interpolate between current points - Location interpolated = interpolateLocation(start, end, progress); - display.teleport(interpolated); - - currentTick++; - if (currentTick >= (currentStep + 1) * ticksPerStep) { - currentStep++; - } - } - }.runTaskTimer(plugin, 0L, 1L); - - animationTasks.put(displayId, task); - return task; - } - - /** - * Animates a transformation of a display entity. - * - * @param display The display entity to animate - * @param startTransform The starting transformation - * @param endTransform The ending transformation - * @param durationTicks The duration of the animation in ticks - * @return A BukkitTask representing the animation task - */ - public BukkitTask animateTransformation(Display display, Transformation startTransform, - Transformation endTransform, long durationTicks) { - final UUID displayId = display.getUniqueId(); - - // Cancel any existing animation for this display - cancelAnimation(display); - - BukkitTask task = new BukkitRunnable() { - long tick = 0; - - @Override - public void run() { - if (tick > durationTicks || !display.isValid()) { - display.setTransformation(endTransform); - cancelAnimation(display); - return; - } - - // Calculate progress (0.0 to 1.0) - float progress = (float) tick / durationTicks; - - // Interpolate transformation - Transformation interpolated = interpolateTransformation(startTransform, endTransform, progress); - display.setTransformation(interpolated); - - tick++; - } - }.runTaskTimer(plugin, 0L, 1L); - - animationTasks.put(displayId, task); - return task; - } - - /** - * Rotates a display entity around an axis. - * - * @param display The display entity to rotate - * @param axis The axis of rotation (should be normalized) - * @param degreesPerSecond The rotation speed in degrees per second - * @return A BukkitTask representing the rotation task - */ - public BukkitTask rotate(Display display, Vector3f axis, float degreesPerSecond) { - final UUID displayId = display.getUniqueId(); - - // Cancel any existing animation for this display - cancelAnimation(display); - - BukkitTask task = new BukkitRunnable() { - @Override - public void run() { - if (!display.isValid()) { - cancelAnimation(display); - return; - } - - // Calculate rotation per tick (20 ticks per second) - float rotationRadiansPerTick = (float) Math.toRadians(degreesPerSecond / 20.0f); - - // Get current transformation - Transformation transform = display.getTransformation(); - - // Create rotation quaternion for this tick - Quaternionf rotation = new Quaternionf().rotationAxis(rotationRadiansPerTick, axis); - - // Apply rotation to current left rotation - Quaternionf newRotation = new Quaternionf(transform.getLeftRotation()).mul(rotation); - - // Apply new transformation - display.setTransformation(new Transformation( - transform.getTranslation(), - newRotation, - transform.getScale(), - transform.getRightRotation() - )); - } - }.runTaskTimer(plugin, 0L, 1L); - - animationTasks.put(displayId, task); - return task; - } - - /** - * Cancels any ongoing animation for a display entity. - * - * @param display The display entity - */ - public void cancelAnimation(Display display) { - UUID displayId = display.getUniqueId(); - BukkitTask task = animationTasks.remove(displayId); - if (task != null) { - task.cancel(); - } - } - - /** - * Cancels all ongoing animations. - */ - public void cancelAllAnimations() { - for (BukkitTask task : animationTasks.values()) { - task.cancel(); - } - animationTasks.clear(); - } - - /** - * Interpolates between two locations. - * - * @param start The starting location - * @param end The ending location - * @param progress The progress from 0.0 to 1.0 - * @return An interpolated location - */ - private Location interpolateLocation(Location start, Location end, float progress) { - 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; - - // Also interpolate pitch and yaw if needed - float pitch = start.getPitch() + (end.getPitch() - start.getPitch()) * progress; - float yaw = start.getYaw() + (end.getYaw() - start.getYaw()) * progress; - - return new Location(start.getWorld(), x, y, z, yaw, pitch); - } - - /** - * Interpolates between two transformations. - * - * @param start The starting transformation - * @param end The ending transformation - * @param progress The progress from 0.0 to 1.0 - * @return An interpolated transformation - */ - private Transformation interpolateTransformation(Transformation start, Transformation end, float progress) { - // Interpolate translation - Vector3f startTranslation = start.getTranslation(); - Vector3f endTranslation = end.getTranslation(); - Vector3f interpolatedTranslation = new Vector3f(startTranslation) - .lerp(endTranslation, progress); - - // Interpolate left rotation (quaternion slerp) - Quaternionf startLeftRotation = start.getLeftRotation(); - Quaternionf endLeftRotation = end.getLeftRotation(); - Quaternionf interpolatedLeftRotation = new Quaternionf(startLeftRotation) - .slerp(endLeftRotation, progress); - - // Interpolate scale - Vector3f startScale = start.getScale(); - Vector3f endScale = end.getScale(); - Vector3f interpolatedScale = new Vector3f(startScale) - .lerp(endScale, progress); - - // Interpolate right rotation - Quaternionf startRightRotation = start.getRightRotation(); - Quaternionf endRightRotation = end.getRightRotation(); - Quaternionf interpolatedRightRotation = new Quaternionf(startRightRotation) - .slerp(endRightRotation, progress); - - return new Transformation( - interpolatedTranslation, - interpolatedLeftRotation, - interpolatedScale, - interpolatedRightRotation - ); - } - } - - /** - * Utility class for creating complex shapes and arrangements using display entities. - */ - public static class ShapeFactory { - private final Plugin plugin; - - /** - * Creates a new ShapeFactory. - * - * @param plugin The plugin instance - */ - public ShapeFactory(Plugin plugin) { - this.plugin = plugin; - } - - /** - * Creates a line of display entities. - * - * @param start The starting location - * @param end The ending location - * @param count The number of entities in the line - * @param creator A function that creates each display entity - * @return A list of created display entities - */ - public List createLine(Location start, Location end, int count, - DisplayCreator creator) { - List displays = new ArrayList<>(); - - for (int i = 0; i < count; i++) { - float progress = (float) i / (count - 1); - Location position = interpolateLocation(start, end, progress); - T display = creator.create(position); - displays.add(display); - } - - return displays; - } - - /** - * Creates a circle of display entities. - * - * @param center The center location of the circle - * @param radius The radius of the circle - * @param count The number of entities in the circle - * @param creator A function that creates each display entity - * @return A list of created display entities - */ - public List createCircle(Location center, double radius, int count, - DisplayCreator creator) { - List displays = new ArrayList<>(); - World world = center.getWorld(); - - for (int i = 0; i < count; i++) { - double angle = 2 * Math.PI * i / count; - double x = center.getX() + radius * Math.cos(angle); - double z = center.getZ() + radius * Math.sin(angle); - - Location position = new Location(world, x, center.getY(), z); - T display = creator.create(position); - displays.add(display); - } - - return displays; - } - - /** - * Creates a sphere of display entities. - * - * @param center The center location of the sphere - * @param radius The radius of the sphere - * @param rings The number of horizontal rings - * @param count The number of entities per ring - * @param creator A function that creates each display entity - * @return A list of created display entities - */ - public List createSphere(Location center, double radius, int rings, int count, - DisplayCreator creator) { - List displays = new ArrayList<>(); - World world = center.getWorld(); - - for (int ring = 0; ring <= rings; ring++) { - double phi = Math.PI * ring / rings; - double y = center.getY() + radius * Math.cos(phi); - double ringRadius = radius * Math.sin(phi); - - int pointsInRing = (ring == 0 || ring == rings) ? 1 : count; - - for (int i = 0; i < pointsInRing; i++) { - double theta = 2 * Math.PI * i / pointsInRing; - double x = center.getX() + ringRadius * Math.cos(theta); - double z = center.getZ() + ringRadius * Math.sin(theta); - - Location position = new Location(world, x, y, z); - T display = creator.create(position); - displays.add(display); - } - } - - return displays; - } - - /** - * Creates a grid of display entities. - * - * @param corner The corner location of the grid - * @param width The width of the grid in blocks - * @param height The height of the grid in blocks - * @param rows The number of rows - * @param columns The number of columns - * @param creator A function that creates each display entity - * @return A list of created display entities - */ - public List createGrid(Location corner, double width, double height, - int rows, int columns, DisplayCreator creator) { - List displays = new ArrayList<>(); - World world = corner.getWorld(); - - double xStep = width / (columns - 1); - double yStep = height / (rows - 1); - - for (int row = 0; row < rows; row++) { - for (int col = 0; col < columns; col++) { - double x = corner.getX() + col * xStep; - double y = corner.getY() + row * yStep; - - Location position = new Location(world, x, y, corner.getZ()); - T display = creator.create(position); - displays.add(display); - } - } - - return displays; - } - - /** - * Creates a cube of display entities. - * - * @param corner The corner location of the cube - * @param size The size of the cube - * @param count The number of entities per edge - * @param creator A function that creates each display entity - * @return A list of created display entities - */ - public List createCube(Location corner, double size, int count, - DisplayCreator creator) { - List displays = new ArrayList<>(); - World world = corner.getWorld(); - - double step = size / (count - 1); - - for (int i = 0; i < count; i++) { - for (int j = 0; j < count; j++) { - // Bottom face - displays.add(creator.create(new Location(world, - corner.getX() + i * step, - corner.getY(), - corner.getZ() + j * step))); - - // Top face - displays.add(creator.create(new Location(world, - corner.getX() + i * step, - corner.getY() + size, - corner.getZ() + j * step))); - - // Four edges - if (j == 0 || j == count - 1) { - for (int k = 1; k < count - 1; k++) { - displays.add(creator.create(new Location(world, - corner.getX() + i * step, - corner.getY() + k * step, - corner.getZ() + j * step))); - } - } - } - - // Remaining four edges - if (i == 0 || i == count - 1) { - for (int k = 1; k < count - 1; k++) { - for (int j = 1; j < count - 1; j++) { - displays.add(creator.create(new Location(world, - corner.getX() + i * step, - corner.getY() + k * step, - corner.getZ() + j * step))); - } - } - } - } - - return displays; - } - - /** - * Creates a text marquee that scrolls text. - * - * @param location The location for the marquee - * @param text The text to scroll - * @param width The width of the marquee in blocks - * @param scrollSpeed The scroll speed in blocks per second - * @return The created TextDisplay entity - */ - public TextDisplay createMarquee(Location location, String text, double width, double scrollSpeed) { - TextDisplay display = createTextDisplay(location, text); - - // Initialize at starting position - display.setTransformation(new Transformation( - new Vector3f((float) width, 0, 0), // Start off-screen to the right - new Quaternionf(), - new Vector3f(1, 1, 1), - new Quaternionf() - )); - - // Calculate total distance to travel (width + text width) - // This is an approximation as exact text width depends on font - float approximateCharWidth = 0.1f; // Approximation - float textWidth = text.length() * approximateCharWidth; - float totalDistance = (float) width + textWidth; - - // Calculate time to complete one cycle in ticks - long ticksPerCycle = (long) (totalDistance / scrollSpeed * 20); // 20 ticks per second - - new BukkitRunnable() { - float position = (float) width; // Start at right edge - - @Override - public void run() { - if (!display.isValid()) { - this.cancel(); - return; - } - - // Update position - position -= scrollSpeed / 20; // Divide by ticks per second - - // Reset position when text has scrolled past the left edge - if (position < -textWidth) { - position = (float) width; - } - - // Update transformation - Transformation transform = display.getTransformation(); - display.setTransformation(new Transformation( - new Vector3f(position, 0, 0), - transform.getLeftRotation(), - transform.getScale(), - transform.getRightRotation() - )); - } - }.runTaskTimer(plugin, 0L, 1L); - - return display; - } - - private Location interpolateLocation(Location start, Location end, float progress) { - 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; - - return new Location(start.getWorld(), x, y, z); - } - } - - /** - * Functional interface for creating display entities. - * - * @param The type of display entity to create - */ - @FunctionalInterface - public interface DisplayCreator { - /** - * Creates a display entity at the specified location. - * - * @param location The location to create the entity - * @return The created display entity - */ - T create(Location location); - } - - /** - * Utility class for creating particle effects with display entities. - */ - public static class ParticleEmitter { - private final Plugin plugin; - private final List particles = new ArrayList<>(); - private BukkitTask emitterTask; - private final Location origin; - private final DisplayCreator particleCreator; - private final int maxParticles; - private final double spawnRadius; - private final double velocity; - private final int lifetime; - private final Random random = new Random(); - - /** - * Creates a new particle emitter. - * - * @param plugin The plugin instance - * @param location The origin location for particles - * @param particleCreator A function that creates each particle entity - * @param maxParticles The maximum number of particles to display at once - * @param spawnRadius The radius in which particles spawn - * @param velocity The velocity of particles - * @param lifetime The lifetime of particles in ticks - */ - public ParticleEmitter(Plugin plugin, Location location, DisplayCreator particleCreator, - int maxParticles, double spawnRadius, double velocity, int lifetime) { - this.plugin = plugin; - this.origin = location.clone(); - this.particleCreator = particleCreator; - this.maxParticles = maxParticles; - this.spawnRadius = spawnRadius; - this.velocity = velocity; - this.lifetime = lifetime; - } - - /** - * Starts emitting particles. - * - * @param rate The number of particles to spawn per second - * @return This emitter for chaining - */ - public ParticleEmitter start(double rate) { - // Calculate ticks between spawns - long ticksBetweenSpawns = Math.max(1, (long) (20 / rate)); - - emitterTask = new BukkitRunnable() { - @Override - public void run() { - spawnParticle(); - - // Remove expired particles - long currentTime = System.currentTimeMillis(); - Iterator iterator = particles.iterator(); - while (iterator.hasNext()) { - Display particle = iterator.next(); - if (!particle.isValid() || - currentTime - particle.getTicksLived() * 50 > lifetime * 50) { - particle.remove(); - iterator.remove(); - } - } - } - }.runTaskTimer(plugin, 0, ticksBetweenSpawns); - - return this; - } - - /** - * Stops emitting particles. - * - * @param removeExisting Whether to remove existing particles - */ - public void stop(boolean removeExisting) { - if (emitterTask != null) { - emitterTask.cancel(); - emitterTask = null; - } - - if (removeExisting) { - for (Display particle : particles) { - if (particle.isValid()) { - particle.remove(); - } - } - particles.clear(); - } - } - - /** - * Spawns a single particle. - */ - private void spawnParticle() { - if (particles.size() >= maxParticles) { - // Remove oldest particle if at max capacity - Display oldest = particles.remove(0); - if (oldest.isValid()) { - oldest.remove(); - } - } - - // Generate random position within spawn radius - double angle = random.nextDouble() * 2 * Math.PI; - double distance = random.nextDouble() * spawnRadius; - double x = origin.getX() + distance * Math.cos(angle); - double y = origin.getY(); - double z = origin.getZ() + distance * Math.sin(angle); - - Location spawnLoc = new Location(origin.getWorld(), x, y, z); - Display particle = particleCreator.create(spawnLoc); - - // Set random velocity - double vx = velocity * (random.nextDouble() - 0.5); - double vy = velocity * random.nextDouble(); - double vz = velocity * (random.nextDouble() - 0.5); - - // Store initial velocity in metadata for animation - particle.setMetadata("velocity", new org.bukkit.metadata.FixedMetadataValue( - plugin, new Vector(vx, vy, vz))); - - // Animate the particle - new BukkitRunnable() { - int tick = 0; - - @Override - public void run() { - if (!particle.isValid() || tick > lifetime) { - this.cancel(); - return; - } - - // Get velocity from metadata - Vector vel = (Vector) particle.getMetadata("velocity").get(0).value(); - - // Apply velocity and gravity - Location current = particle.getLocation(); - vel.setY(vel.getY() - 0.05); // Apply gravity - - // Move particle - particle.teleport(current.add(vel)); - - // Scale down as it ages - float scale = 1.0f - ((float) tick / lifetime) * 0.5f; - Transformation transform = particle.getTransformation(); - particle.setTransformation(new Transformation( - transform.getTranslation(), - transform.getLeftRotation(), - new Vector3f(scale, scale, scale), - transform.getRightRotation() - )); - - tick++; - } - }.runTaskTimer(plugin, 0L, 1L); - - particles.add(particle); - } - } - - /** - * Utility class for creating holographic displays with text and items. - */ - public static class Hologram { - private final List displays = new ArrayList<>(); - private final Location location; - private final double lineHeight; - - /** - * Creates a new hologram. - * - * @param location The location of the hologram - * @param lineHeight The height between each line - */ - public Hologram(Location location, double lineHeight) { - this.location = location.clone(); - this.lineHeight = lineHeight; - } - - /** - * Adds a text line to the hologram. - * - * @param text The text to display - * @return This hologram for chaining - */ - public Hologram addTextLine(String text) { - Location lineLoc = getNextLineLocation(); - TextDisplay display = createTextDisplay(lineLoc, text); - - // Center the text - display.setAlignment(TextDisplay.TextAlignment.CENTER); - - // Make it visible from all sides - display.setBillboard(Display.Billboard.CENTER); - - // Set see-through background - display.setDefaultBackground(false); - - displays.add(display); - return this; - } - - /** - * Adds an item line to the hologram. - * - * @param item The item to display - * @return This hologram for chaining - */ - public Hologram addItemLine(org.bukkit.inventory.ItemStack item) { - Location lineLoc = getNextLineLocation(); - ItemDisplay display = createItemDisplay(lineLoc, item); - - // Make it visible from all sides - display.setBillboard(Display.Billboard.CENTER); - - // Scale down to a reasonable size - Transformation transform = display.getTransformation(); - display.setTransformation(new Transformation( - transform.getTranslation(), - transform.getLeftRotation(), - new Vector3f(0.6f, 0.6f, 0.6f), - transform.getRightRotation() - )); - - displays.add(display); - return this; - } - - /** - * Adds a block line to the hologram. - * - * @param blockData The block data to display - * @return This hologram for chaining - */ - public Hologram addBlockLine(org.bukkit.block.data.BlockData blockData) { - Location lineLoc = getNextLineLocation(); - BlockDisplay display = createBlockDisplay(lineLoc, blockData); - - // Make it visible from all sides - display.setBillboard(Display.Billboard.CENTER); - - // Scale down to a reasonable size - Transformation transform = display.getTransformation(); - display.setTransformation(new Transformation( - transform.getTranslation(), - transform.getLeftRotation(), - new Vector3f(0.4f, 0.4f, 0.4f), - transform.getRightRotation() - )); - - displays.add(display); - return this; - } - - /** - * Updates a text line in the hologram. - * - * @param index The index of the line to update - * @param newText The new text for the line - * @return This hologram for chaining - */ - public Hologram updateTextLine(int index, String newText) { - if (index < 0 || index >= displays.size()) { - throw new IndexOutOfBoundsException("Line index out of range"); - } - - Display display = displays.get(index); - if (display instanceof TextDisplay) { - ((TextDisplay) display).setText(newText); - } else { - throw new IllegalArgumentException("The specified line is not a text line"); - } - - return this; - } - - /** - * Updates an item line in the hologram. - * - * @param index The index of the line to update - * @param newItem The new item for the line - * @return This hologram for chaining - */ - public Hologram updateItemLine(int index, org.bukkit.inventory.ItemStack newItem) { - if (index < 0 || index >= displays.size()) { - throw new IndexOutOfBoundsException("Line index out of range"); - } - - Display display = displays.get(index); - if (display instanceof ItemDisplay) { - ((ItemDisplay) display).setItemStack(newItem); - } else { - throw new IllegalArgumentException("The specified line is not an item line"); - } - - return this; - } - - /** - * Updates a block line in the hologram. - * - * @param index The index of the line to update - * @param newBlockData The new block data for the line - * @return This hologram for chaining - */ - public Hologram updateBlockLine(int index, org.bukkit.block.data.BlockData newBlockData) { - if (index < 0 || index >= displays.size()) { - throw new IndexOutOfBoundsException("Line index out of range"); - } - - Display display = displays.get(index); - if (display instanceof BlockDisplay) { - ((BlockDisplay) display).setBlock(newBlockData); - } else { - throw new IllegalArgumentException("The specified line is not a block line"); - } - - return this; - } - - /** - * Removes a line from the hologram. - * - * @param index The index of the line to remove - * @return This hologram for chaining - */ - public Hologram removeLine(int index) { - if (index < 0 || index >= displays.size()) { - throw new IndexOutOfBoundsException("Line index out of range"); - } - - Display display = displays.remove(index); - if (display.isValid()) { - display.remove(); - } - - // Update positions of subsequent lines - for (int i = index; i < displays.size(); i++) { - Display d = displays.get(i); - Location newLoc = location.clone().add(0, -i * lineHeight, 0); - d.teleport(newLoc); - } - - return this; - } - - /** - * Gets the next line location based on the number of existing lines. - * - * @return The location for the next line - */ - private Location getNextLineLocation() { - int lineNumber = displays.size(); - return location.clone().add(0, -lineNumber * lineHeight, 0); - } - - /** - * Removes the entire hologram. - */ - public void remove() { - for (Display display : displays) { - if (display.isValid()) { - display.remove(); - } - } - displays.clear(); - } - - /** - * Gets all display entities in this hologram. - * - * @return A list of all display entities - */ - public List getDisplays() { - return Collections.unmodifiableList(displays); - } - } - - /** - * Utility class for creating 3D models using display entities. - */ - public static class ModelBuilder { - private final List parts = new ArrayList<>(); - private final Location origin; - private final World world; - - /** - * Creates a new model builder. - * - * @param origin The origin location for the model - */ - public ModelBuilder(Location origin) { - this.origin = origin.clone(); - this.world = origin.getWorld(); - } - - /** - * Adds a text part to the model. - * - * @param text The text to display - * @param x The relative X position - * @param y The relative Y position - * @param z The relative Z position - * @param scale The scale of the part - * @return This model builder for chaining - */ - public ModelBuilder addTextPart(String text, double x, double y, double z, float scale) { - Location partLoc = origin.clone().add(x, y, z); - TextDisplay display = createTextDisplay(partLoc, text); - - // Apply scale - Transformation transform = display.getTransformation(); - display.setTransformation(new Transformation( - transform.getTranslation(), - transform.getLeftRotation(), - new Vector3f(scale, scale, scale), - transform.getRightRotation() - )); - - parts.add(display); - return this; - } - - /** - * Adds an item part to the model. - * - * @param item The item to display - * @param x The relative X position - * @param y The relative Y position - * @param z The relative Z position - * @param scale The scale of the part - * @param rotation A quaternion representing the rotation - * @return This model builder for chaining - */ - public ModelBuilder addItemPart(org.bukkit.inventory.ItemStack item, double x, double y, double z, - float scale, Quaternionf rotation) { - Location partLoc = origin.clone().add(x, y, z); - ItemDisplay display = createItemDisplay(partLoc, item); - - // Apply scale and rotation - Transformation transform = display.getTransformation(); - display.setTransformation(new Transformation( - transform.getTranslation(), - rotation, - new Vector3f(scale, scale, scale), - transform.getRightRotation() - )); - - parts.add(display); - return this; - } - - /** - * Adds a block part to the model. - * - * @param blockData The block data to display - * @param x The relative X position - * @param y The relative Y position - * @param z The relative Z position - * @param scale The scale of the part - * @param rotation A quaternion representing the rotation - * @return This model builder for chaining - */ - public ModelBuilder addBlockPart(org.bukkit.block.data.BlockData blockData, double x, double y, double z, - float scale, Quaternionf rotation) { - Location partLoc = origin.clone().add(x, y, z); - BlockDisplay display = createBlockDisplay(partLoc, blockData); - - // Apply scale and rotation - Transformation transform = display.getTransformation(); - display.setTransformation(new Transformation( - transform.getTranslation(), - rotation, - new Vector3f(scale, scale, scale), - transform.getRightRotation() - )); - - parts.add(display); - return this; - } - - /** - * Rotates the entire model around a specified axis. - * - * @param axis The axis of rotation - * @param angleRadians The angle in radians - * @return This model builder for chaining - */ - public ModelBuilder rotate(Vector3f axis, float angleRadians) { - Quaternionf rotation = new Quaternionf().rotationAxis(angleRadians, axis); - - for (Display part : parts) { - // Calculate new position relative to origin - Location partLoc = part.getLocation(); - double dx = partLoc.getX() - origin.getX(); - double dy = partLoc.getY() - origin.getY(); - double dz = partLoc.getZ() - origin.getZ(); - - // Create a vector for the position - Vector3f pos = new Vector3f((float) dx, (float) dy, (float) dz); - - // Rotate the position - pos.rotate(rotation); - - // Update the part's location - Location newLoc = origin.clone().add(pos.x, pos.y, pos.z); - part.teleport(newLoc); - - // Also update the part's own rotation - Transformation transform = part.getTransformation(); - Quaternionf newRotation = new Quaternionf(transform.getLeftRotation()).mul(rotation); - - part.setTransformation(new Transformation( - transform.getTranslation(), - newRotation, - transform.getScale(), - transform.getRightRotation() - )); - } - - return this; - } - - /** - * Moves the entire model. - * - * @param dx The X offset - * @param dy The Y offset - * @param dz The Z offset - * @return This model builder for chaining - */ - public ModelBuilder move(double dx, double dy, double dz) { - origin.add(dx, dy, dz); - - for (Display part : parts) { - Location partLoc = part.getLocation(); - partLoc.add(dx, dy, dz); - part.teleport(partLoc); - } - - return this; - } - - /** - * Scales the entire model. - * - * @param factor The scale factor - * @return This model builder for chaining - */ - public ModelBuilder scale(float factor) { - for (Display part : parts) { - // Update position relative to origin - Location partLoc = part.getLocation(); - double dx = partLoc.getX() - origin.getX(); - double dy = partLoc.getY() - origin.getY(); - double dz = partLoc.getZ() - origin.getZ(); - - // Scale the position - dx *= factor; - dy *= factor; - dz *= factor; - - // Update location - Location newLoc = origin.clone().add(dx, dy, dz); - part.teleport(newLoc); - - // Update scale - Transformation transform = part.getTransformation(); - Vector3f currentScale = transform.getScale(); - Vector3f newScale = new Vector3f( - currentScale.x * factor, - currentScale.y * factor, - currentScale.z * factor - ); - - part.setTransformation(new Transformation( - transform.getTranslation(), - transform.getLeftRotation(), - newScale, - transform.getRightRotation() - )); - } - - return this; - } - - /** - * Removes the entire model. - */ - public void remove() { - for (Display part : parts) { - if (part.isValid()) { - part.remove(); - } - } - parts.clear(); - } - - /** - * Gets all display entities in this model. - * - * @return A list of all display entities - */ - public List getParts() { - return Collections.unmodifiableList(parts); - } - } - - /** - * Utility class for creating display entity based UIs. - */ - public static class UIBuilder { - private final Plugin plugin; - private final Location origin; - private final List elements = new ArrayList<>(); - private final Map namedElements = new HashMap<>(); - - /** - * Creates a new UI builder. - * - * @param plugin The plugin instance - * @param origin The origin location for the UI - */ - public UIBuilder(Plugin plugin, Location origin) { - this.plugin = plugin; - this.origin = origin.clone(); - } - - /** - * Adds a text element to the UI. - * - * @param id A unique identifier for the element - * @param text The text to display - * @param x The relative X position - * @param y The relative Y position - * @param alignment The text alignment - * @return This UI builder for chaining - */ - public UIBuilder addTextElement(String id, String text, double x, double y, TextDisplay.TextAlignment alignment) { - Location elemLoc = origin.clone().add(x, y, 0); - TextDisplay display = createTextDisplay(elemLoc, text); - - // Configure the text display - display.setAlignment(alignment); - display.setBillboard(Display.Billboard.FIXED); - display.setDefaultBackground(false); - - elements.add(display); - namedElements.put(id, display); - return this; - } - - /** - * Adds an item element to the UI. - * - * @param id A unique identifier for the element - * @param item The item to display - * @param x The relative X position - * @param y The relative Y position - * @param scale The scale of the item - * @return This UI builder for chaining - */ - public UIBuilder addItemElement(String id, org.bukkit.inventory.ItemStack item, double x, double y, float scale) { - Location elemLoc = origin.clone().add(x, y, 0); - ItemDisplay display = createItemDisplay(elemLoc, item); - - // Configure the item display - display.setBillboard(Display.Billboard.FIXED); - - // Apply scale - Transformation transform = display.getTransformation(); - display.setTransformation(new Transformation( - transform.getTranslation(), - transform.getLeftRotation(), - new Vector3f(scale, scale, scale), - transform.getRightRotation() - )); - - elements.add(display); - namedElements.put(id, display); - return this; - } - - /** - * Adds a block element to the UI. - * - * @param id A unique identifier for the element - * @param blockData The block data to display - * @param x The relative X position - * @param y The relative Y position - * @param scale The scale of the block - * @return This UI builder for chaining - */ - public UIBuilder addBlockElement(String id, org.bukkit.block.data.BlockData blockData, double x, double y, float scale) { - Location elemLoc = origin.clone().add(x, y, 0); - BlockDisplay display = createBlockDisplay(elemLoc, blockData); - - // Configure the block display - display.setBillboard(Display.Billboard.FIXED); - - // Apply scale - Transformation transform = display.getTransformation(); - display.setTransformation(new Transformation( - transform.getTranslation(), - transform.getLeftRotation(), - new Vector3f(scale, scale, scale), - transform.getRightRotation() - )); - - elements.add(display); - namedElements.put(id, display); - return this; - } - - /** - * Updates a text element in the UI. - * - * @param id The identifier of the element to update - * @param newText The new text for the element - * @return This UI builder for chaining - */ - public UIBuilder updateTextElement(String id, String newText) { - Display element = namedElements.get(id); - if (element == null) { - throw new IllegalArgumentException("No element found with ID: " + id); - } - - if (element instanceof TextDisplay) { - ((TextDisplay) element).setText(newText); - } else { - throw new IllegalArgumentException("Element with ID " + id + " is not a text element"); - } - - return this; - } - - /** - * Updates an item element in the UI. - * - * @param id The identifier of the element to update - * @param newItem The new item for the element - * @return This UI builder for chaining - */ - public UIBuilder updateItemElement(String id, org.bukkit.inventory.ItemStack newItem) { - Display element = namedElements.get(id); - if (element == null) { - throw new IllegalArgumentException("No element found with ID: " + id); - } - - if (element instanceof ItemDisplay) { - ((ItemDisplay) element).setItemStack(newItem); - } else { - throw new IllegalArgumentException("Element with ID " + id + " is not an item element"); - } - - return this; - } - - /** - * Updates a block element in the UI. - * - * @param id The identifier of the element to update - * @param newBlockData The new block data for the element - * @return This UI builder for chaining - */ - public UIBuilder updateBlockElement(String id, org.bukkit.block.data.BlockData newBlockData) { - Display element = namedElements.get(id); - if (element == null) { - throw new IllegalArgumentException("No element found with ID: " + id); - } - - if (element instanceof BlockDisplay) { - ((BlockDisplay) element).setBlock(newBlockData); - } else { - throw new IllegalArgumentException("Element with ID " + id + " is not a block element"); - } - - return this; - } - - /** - * Moves the entire UI. - * - * @param newLocation The new location for the UI origin - * @return This UI builder for chaining - */ - public UIBuilder moveTo(Location newLocation) { - // Calculate offset - double dx = newLocation.getX() - origin.getX(); - double dy = newLocation.getY() - origin.getY(); - double dz = newLocation.getZ() - origin.getZ(); - - // Update origin - origin.setWorld(newLocation.getWorld()); - origin.setX(newLocation.getX()); - origin.setY(newLocation.getY()); - origin.setZ(newLocation.getZ()); - - // Move all elements - for (Display element : elements) { - Location elemLoc = element.getLocation(); - elemLoc.add(dx, dy, dz); - element.teleport(elemLoc); - } - - return this; - } - - /** - * Removes the entire UI. - */ - public void remove() { - for (Display element : elements) { - if (element.isValid()) { - element.remove(); - } - } - elements.clear(); - namedElements.clear(); - } - - /** - * Gets a specific element by its ID. - * - * @param id The ID of the element - * @return The display entity, or null if not found - */ - public Display getElement(String id) { - return namedElements.get(id); - } - - /** - * Gets all elements in this UI. - * - * @return A list of all display entities - */ - public List getAllElements() { - return Collections.unmodifiableList(elements); - } - } -} \ No newline at end of file diff --git a/src/main/java/me/trouper/trimserver/utils/visual/DisplayUtils.java b/src/main/java/me/trouper/trimserver/utils/visual/DisplayUtils.java index aa7f637..788c9a2 100755 --- a/src/main/java/me/trouper/trimserver/utils/visual/DisplayUtils.java +++ b/src/main/java/me/trouper/trimserver/utils/visual/DisplayUtils.java @@ -46,9 +46,12 @@ public class DisplayUtils implements Main { public static void sphereWave(Location center, double maxRadius, double radialStep, double maxDistanceBetweenPoints, Consumer action) { AtomicReference currentRadius = new AtomicReference<>(radialStep); - Bukkit.getScheduler().scheduleSyncRepeatingTask(main.getPlugin(), () -> { + Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task) -> { double r = currentRadius.get(); - if (r > maxRadius) return; + if (r > maxRadius) { + task.cancel(); + return; + } sphere(center, r, maxDistanceBetweenPoints, action); currentRadius.set(r + radialStep); @@ -92,8 +95,9 @@ public class DisplayUtils implements Main { public static void wave(Location loc, double radius, Consumer action, double gap) { AtomicReference i = new AtomicReference<>(gap); - Bukkit.getScheduler().scheduleSyncRepeatingTask(main.getPlugin(), () -> { + Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task) -> { if (i.get() >= radius) { + task.cancel(); return; } ring(loc, i.get(), action); @@ -103,8 +107,11 @@ public class DisplayUtils implements Main { public static void wave(Location loc, double radius, double radialGap, double maxDistanceBetweenPoints, Consumer action) { AtomicReference r = new AtomicReference<>(radialGap); - Bukkit.getScheduler().scheduleSyncRepeatingTask(main.getPlugin(), () -> { - if (r.get() > radius) return; + Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task) -> { + if (r.get() > radius) { + task.cancel(); + return; + } ring(loc, r.get(), maxDistanceBetweenPoints, action); r.set(r.get() + radialGap); }, 0, 1); @@ -197,8 +204,9 @@ public class DisplayUtils implements Main { public static void fanWave(Location loc, double radius, int sections, Consumer action, double gap) { double arcLength = 360.0 / sections; AtomicReference i = new AtomicReference<>(0.0); - Bukkit.getScheduler().scheduleSyncRepeatingTask(main.getPlugin(), () -> { + Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task) -> { if (i.get() >= 360) { + task.cancel(); return; } double start = i.get(); @@ -216,8 +224,9 @@ public class DisplayUtils implements Main { AtomicInteger i = new AtomicInteger(0); Randomizer random = new Randomizer(); - Bukkit.getScheduler().scheduleSyncRepeatingTask(main.getPlugin(), () -> { + Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task) -> { if (i.get() >= sections) { + task.cancel(); return; } double start = random.getRandomElement(ints); @@ -229,8 +238,11 @@ public class DisplayUtils implements Main { public static void waveFan(Location loc, double radius, int angleFrom, int angleTo, double maxDistanceBetweenPoints, Consumer action, double radialGap) { AtomicReference r = new AtomicReference<>(radialGap); - Bukkit.getScheduler().scheduleSyncRepeatingTask(main.getPlugin(), () -> { - if (r.get() >= radius) return; + Bukkit.getScheduler().runTaskTimer(main.getPlugin(), (task) -> { + if (r.get() >= radius) { + task.cancel(); + return; + } arc(loc, r.get(), angleFrom, angleTo, maxDistanceBetweenPoints, action); r.set(r.get() + radialGap); }, 0, 1);