Updated wand to make behavior more consistent.

This commit is contained in:
thetrouper
2025-06-20 19:01:32 -05:00
parent dbe8febac9
commit 49113e9660
6 changed files with 225 additions and 56 deletions

View File

@@ -2,17 +2,26 @@ package me.trouper.alias.server.systems;
import me.trouper.alias.server.Main;
import me.trouper.alias.server.events.QuickListener;
import me.trouper.alias.utils.misc.Cooldown;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.player.*;
import org.bukkit.inventory.ItemStack;
import java.util.UUID;
public abstract class AbstractWand implements QuickListener, Main {
private final String usePermission;
private final ItemStack wandItem;
private final Cooldown<UUID> debounce = new Cooldown<>();
public AbstractWand(String usePermission, ItemStack wandItem) {
this.wandItem = wandItem.clone();
@@ -37,43 +46,96 @@ public abstract class AbstractWand implements QuickListener, Main {
}
@EventHandler
public void onSwapHands(PlayerSwapHandItemsEvent e) {
public final void onSwapHands(PlayerSwapHandItemsEvent e) {
Player p = e.getPlayer();
if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return;
e.setCancelled(true);
if (p.isSneaking()) onSwapHandSneak(p);
else onSwapHand(p);
swapHand(p);
}
@EventHandler
public void onAnimate(PlayerAnimationEvent e) {
Player p = e.getPlayer();
if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return;
if (!e.getAnimationType().equals(PlayerAnimationType.ARM_SWING)) return;
if (e.getPlayer().getTargetEntity(5) == null) return;
e.setCancelled(true);
if (p.isSneaking()) onLeftClickSneak(p);
else onLeftClick(p);
}
@EventHandler
public void onInteract(PlayerInteractEvent e) {
@EventHandler(priority = EventPriority.HIGHEST)
public final void onInteract(PlayerInteractEvent e) {
Player p = e.getPlayer();
if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return;
Action action = e.getAction();
if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) return;
switch (action) {
case RIGHT_CLICK_AIR, RIGHT_CLICK_BLOCK -> {
e.setCancelled(true);
if (debounce.isOnCooldown(p.getUniqueId())) return;
debounce.setCooldown(p.getUniqueId(),100);
rightClick(p);
}
case LEFT_CLICK_AIR, LEFT_CLICK_BLOCK -> {
e.setCancelled(true);
leftClick(p);
}
}
}
@EventHandler(priority = EventPriority.HIGHEST)
public final void onEntityDamage(EntityDamageByEntityEvent e) {
if (!(e.getDamager() instanceof Player p)) return;
if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return;
e.setCancelled(true);
leftClick(p);
}
@EventHandler(priority = EventPriority.HIGHEST)
public final void onEntityInteract(PlayerInteractEntityEvent e) {
Player p = e.getPlayer();
if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return;
e.setCancelled(true);
if (p.isSneaking()) onRightClickSneak(p);
else onRightClick(p);
if (debounce.isOnCooldown(p.getUniqueId())) return;
debounce.setCooldown(p.getUniqueId(),100);
rightClick(p);
}
@EventHandler(priority = EventPriority.HIGHEST)
public final void onEntityInteractAt(PlayerInteractAtEntityEvent e) {
Player p = e.getPlayer();
if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return;
if (debounce.isOnCooldown(p.getUniqueId())) return;
debounce.setCooldown(p.getUniqueId(),100);
e.setCancelled(true);
rightClick(p);
}
@EventHandler(priority = EventPriority.HIGHEST)
public final void onBlockBreak(BlockBreakEvent e) {
Player p = e.getPlayer();
if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return;
e.setCancelled(true);
}
@EventHandler(priority = EventPriority.HIGHEST)
public final void onBlockPlace(BlockPlaceEvent e) {
Player p = e.getPlayer();
if (!isWand(e.getItemInHand()) || !p.hasPermission(getUsePermission())) return;
e.setCancelled(true);
}
@EventHandler(priority = EventPriority.HIGHEST)
public final void onItemDrop(PlayerDropItemEvent e) {
Player p = e.getPlayer();
if (!isWand(e.getItemDrop().getItemStack()) || !p.hasPermission(getUsePermission())) return;
e.setCancelled(true);
}
@EventHandler
public void onScroll(PlayerItemHeldEvent e) {
public final void onScroll(PlayerItemHeldEvent e) {
Player p = e.getPlayer();
if (!isHoldingWand(p) || !p.hasPermission(getUsePermission())) return;
@@ -81,9 +143,27 @@ public abstract class AbstractWand implements QuickListener, Main {
int curr = e.getNewSlot();
if (!p.isSneaking() || !isWand(p.getInventory().getItem(prev))) return;
e.setCancelled(true);
if (curr < prev) onScrollUp(e.getPlayer());
else if (curr > prev) onScrollDown(e.getPlayer());
if (curr == 8 && prev == 0) onScrollUp(p);
else if (curr == 0 && prev == 8) onScrollDown(p);
else if (curr < prev) onScrollUp(p);
else if (curr > prev) onScrollDown(p);
}
public final void swapHand(Player p) {
if (p.isSneaking()) onSwapHandSneak(p);
else onSwapHand(p);
}
public final void leftClick(Player p) {
if (p.isSneaking()) onLeftClickSneak(p);
else onLeftClick(p);
}
public final void rightClick(Player p) {
if (p.isSneaking()) onRightClickSneak(p);
else onRightClick(p);
}
protected void onSwapHand(Player player) {}
@@ -92,6 +172,14 @@ public abstract class AbstractWand implements QuickListener, Main {
protected void onRightClickSneak(Player player) {}
protected void onLeftClick(Player player) {}
protected void onLeftClickSneak(Player player) {}
/**
* The player must be sneaking to scroll the wand.
*/
protected void onScrollUp(Player player) {}
/**
* The player must be sneaking to scroll the wand.
*/
protected void onScrollDown(Player player) {}
}

View File

@@ -74,26 +74,6 @@ public class Text implements Main {
message(pallet,true,audience,text,args);
}
public static void sendWarning(Audience audience, String warning, Object... args) {
messageAny(Pallet.WARNING, audience, warning, args);
}
public static void sendError(Audience audience, String error, Object... args) {
messageAny(Pallet.ERROR, audience, error, args);
}
public static void sendInfo(Audience audience, String info, Object... args) {
messageAny(Pallet.INFO, audience, info, args);
}
public static void sendSuccess(Audience audience, String success, Object... args) {
messageAny(Pallet.SUCCESS, audience, success, args);
}
public static void sendMessage(Audience audience, String text, Object... args) {
messageAny(Pallet.NEUTRAL, audience, text, args);
}
/**
* Gets the component form of a message, applying pallet formatting to the text and placeholders. Placeholders are zero-indexed and curly braced. {0}, {1}, {2}...
* @param pallet The colors to use for text and arguments.

View File

@@ -5,9 +5,12 @@ import me.trouper.alias.server.systems.Verbose;
import me.trouper.alias.server.systems.TaskManager;
import me.trouper.alias.server.systems.burning.BlockBurner;
import me.trouper.alias.server.systems.burning.BurnOptions;
import me.trouper.alias.utils.TargetingUtils;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
@@ -23,6 +26,7 @@ public class ExplosionUtils implements Main {
private double coreRadius = 3.0;
private double falloffRadius = 8.0;
private double maxBurnRadius = 15.0;
private double baseDamage = 20;
private double destructionDelay = 0.0; // SECONDS
private double burnDelay = 0.5; // SECONDS
private double maxHeat = 1.0;
@@ -40,6 +44,9 @@ public class ExplosionUtils implements Main {
public double getMaxBurnRadius() { return maxBurnRadius; }
public void setMaxBurnRadius(double maxBurnRadius) { this.maxBurnRadius = maxBurnRadius; }
public double getBaseDamage() { return baseDamage; }
public void setBaseDamage(double baseDamage) { this.baseDamage = baseDamage; }
public double getDestructionDelay() { return destructionDelay; }
public void setDestructionDelay(double destructionDelay) { this.destructionDelay = destructionDelay; }
@@ -92,22 +99,22 @@ public class ExplosionUtils implements Main {
}
public void restore() {
// First cleanup all tasks
cleanup();
// Then restore blocks
for (Map.Entry<Block, BlockState> entry : originalStates.entrySet()) {
Block block = entry.getKey();
BlockState snapshot = entry.getValue();
block.setBlockData(snapshot.getBlockData(), false);
snapshot.update(true, false);
Bukkit.getScheduler().runTask(main.getPlugin(),()->{
block.setBlockData(snapshot.getBlockData(), false);
snapshot.update(true, false);
ItemStack[] contents = originalInventories.get(block);
if (contents != null && block.getState() instanceof InventoryHolder) {
Inventory inv = ((InventoryHolder) block.getState()).getInventory();
inv.setContents(contents);
}
ItemStack[] contents = originalInventories.get(block);
if (contents != null && block.getState() instanceof InventoryHolder) {
Inventory inv = ((InventoryHolder) block.getState()).getInventory();
inv.setContents(contents);
}
});
}
}
@@ -127,9 +134,11 @@ public class ExplosionUtils implements Main {
World world = center.getWorld();
if (world == null) throw new IllegalArgumentException("Center location must have a valid world");
Map<Block, Double> affectedBlocks = getBlocksInRadius(center, options.getMaxBurnRadius());
double maxBurnRadius = options.getMaxBurnRadius();
Map<Block, Double> affectedBlocks = getBlocksInRadius(center, maxBurnRadius);
Map<UUID, Double> affectedEntities = getEntitiesInRadius(center, maxBurnRadius);
// Create shared task manager for this explosion
TaskManager sharedTaskManager = new TaskManager();
BlockBurner burner = new BlockBurner(options.getBurnOptions(), sharedTaskManager);
ExplosionResult result = new ExplosionResult(burner);
@@ -147,6 +156,7 @@ public class ExplosionUtils implements Main {
scheduleDestruction(blocksToDestroy, options, sharedTaskManager);
scheduleBurning(blocksToBurn, blocksHeatMap, burner, center, options, sharedTaskManager);
scheduleDamage(affectedEntities, options, sharedTaskManager);
if (options.isCreateParticles() || options.isPlaySound()) createExplosionEffects(center, options);
@@ -180,6 +190,17 @@ public class ExplosionUtils implements Main {
return blocks;
}
public static Map<UUID, Double> getEntitiesInRadius(Location center, double radius) {
List<LivingEntity> rawList = center.getNearbyLivingEntities(radius).stream().toList();
Map<UUID, Double> entities = new HashMap<>();
for (LivingEntity livingEntity : rawList) {
entities.put(livingEntity.getUniqueId(),livingEntity.getLocation().distance(center));
}
return entities;
}
private static void categorizeBlocks(Map<Block, Double> affectedBlocks, ExplosionOptions options,
Set<Block> blocksToDestroy, Set<Block> blocksToBurn,
Map<Block, Float> blocksHeatMap) {
@@ -296,6 +317,41 @@ public class ExplosionUtils implements Main {
}, burnDelayTicks);
}
private static void scheduleDamage(Map<UUID, Double> affected, ExplosionOptions options, TaskManager taskManager) {
double baseDamage = options.getBaseDamage();
double igniteDistance = options.getMaxBurnRadius();
double halfDamageDistance = options.getFalloffRadius();
double fullDamageDistance = options.getCoreRadius();
for (Map.Entry<UUID, Double> entityDistance : affected.entrySet()) {
LivingEntity liv = (LivingEntity) Bukkit.getEntity(entityDistance.getKey());
if (liv == null) continue;
double distance = entityDistance.getValue();
if (distance >= halfDamageDistance && distance <= igniteDistance) {
taskManager.scheduleTask(()->{
liv.setFireTicks(5 * 20);
},(long) distance / 2);
return;
}
if (distance >= fullDamageDistance && distance <= halfDamageDistance) {
taskManager.scheduleTask(()->{
liv.setFireTicks(10 * 20);
liv.damage(baseDamage / 2);
},(long) distance / 2);
return;
}
if (distance <= fullDamageDistance) {
taskManager.scheduleTask(()->{
liv.setFireTicks(15 * 20);
liv.damage(baseDamage);
},(long) distance / 2);
return;
}
}
}
private static void createExplosionEffects(Location center, ExplosionOptions options) {
World world = center.getWorld();
if (world == null) return;

View File

@@ -0,0 +1,5 @@
package me.trouper.alias.server.systems.world;
public class Snapshot {
}

View File

@@ -0,0 +1,20 @@
package me.trouper.alias.utils;
import org.bukkit.util.Vector;
public class VectorUtils {
public static float[] toAngles(Vector vector) {
double x = vector.getX();
double y = vector.getY();
double z = vector.getZ();
float yaw = (float) Math.toDegrees(Math.atan2(-x, z));
double xzLength = Math.sqrt(x * x + z * z);
float pitch = (float) Math.toDegrees(Math.atan2(-y, xzLength));
return new float[] { yaw, pitch };
}
}

View File

@@ -0,0 +1,20 @@
package me.trouper.alias.utils.misc;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
public class TimeUtils {
public static String formatTime(long millis) {
Instant instant = Instant.ofEpochMilli(millis);
return DateTimeFormatter.RFC_1123_DATE_TIME.withZone(ZoneId.of("UTC")).format(instant);
}
public static long deserializeTime(String rfcString) {
LocalDateTime dateTime = LocalDateTime.parse(rfcString, DateTimeFormatter.RFC_1123_DATE_TIME);
return dateTime.atZone(ZoneId.of("UTC")).toInstant().toEpochMilli();
}
}