diff --git a/pom.xml b/pom.xml index 6b755e0..12486ff 100644 --- a/pom.xml +++ b/pom.xml @@ -99,16 +99,22 @@ - co.aikar - acf-paper - 0.5.0-SNAPSHOT + org.spigotmc + spigot-api + 1.18.1-R0.1-SNAPSHOT + provided org.spigotmc - spigot-api + spigot 1.18.1-R0.1-SNAPSHOT provided + + co.aikar + acf-paper + 0.5.0-SNAPSHOT + org.projectlombok lombok @@ -118,7 +124,7 @@ org.jetbrains annotations - 22.0.0 + 23.0.0 compile @@ -138,11 +144,13 @@ 4.7.0 provided + diff --git a/src/main/java/exposed/hydrogen/nightclub/Nightclub.java b/src/main/java/exposed/hydrogen/nightclub/Nightclub.java index 6092feb..3bd723e 100644 --- a/src/main/java/exposed/hydrogen/nightclub/Nightclub.java +++ b/src/main/java/exposed/hydrogen/nightclub/Nightclub.java @@ -12,7 +12,12 @@ import exposed.hydrogen.nightclub.commands.LightUniverseCommand; import exposed.hydrogen.nightclub.json.LightJSONReader; import exposed.hydrogen.nightclub.json.LightJSONWriter; -import exposed.hydrogen.nightclub.light.*; +import exposed.hydrogen.nightclub.light.Light; +import exposed.hydrogen.nightclub.light.LightUniverse; +import exposed.hydrogen.nightclub.light.LightUniverseManager; +import exposed.hydrogen.nightclub.light.data.LightData; +import exposed.hydrogen.nightclub.light.data.LightPattern; +import exposed.hydrogen.nightclub.light.data.LightType; import exposed.hydrogen.nightclub.light.event.LightChannel; import exposed.hydrogen.nightclub.light.event.LightSpeedChannel; import lombok.Getter; @@ -42,10 +47,10 @@ public final class Nightclub extends JavaPlugin { @Override public void onEnable() { GSON = new GsonBuilder() - .serializeNulls() .setPrettyPrinting() .registerTypeAdapter(LightUniverse.class, new LightUniverse.LightUniverseInstanceCreator()) - .registerTypeAdapter(Light.class, new Light.LightUniverseInstanceCreator()) + .registerTypeAdapter(Light.class, new Light.LightInstanceCreator()) + .registerTypeAdapter(LightData.class, new LightData.LightDataInstanceCreator()) .create(); protocolManager = ProtocolLibrary.getProtocolManager(); diff --git a/src/main/java/exposed/hydrogen/nightclub/beatmap/BeatmapPlayer.java b/src/main/java/exposed/hydrogen/nightclub/beatmap/BeatmapPlayer.java index cc687b6..442d61c 100644 --- a/src/main/java/exposed/hydrogen/nightclub/beatmap/BeatmapPlayer.java +++ b/src/main/java/exposed/hydrogen/nightclub/beatmap/BeatmapPlayer.java @@ -2,6 +2,7 @@ import com.google.gson.JsonArray; import exposed.hydrogen.nightclub.Nightclub; +import exposed.hydrogen.nightclub.light.Light; import exposed.hydrogen.nightclub.light.event.LightChannel; import lombok.Getter; import org.bukkit.entity.Player; @@ -96,7 +97,7 @@ private void handle(LightEvent event, List events) { // Ring spin case 8 -> this.getClass(); // Toggle ring zoom - case 9 -> this.getClass(); + case 9 -> Nightclub.getLightUniverseManager().getLoadedUniverse().getLights().forEach(Light::ringZoom); // Rotation speed multiplier for left lasers case 12 -> LightChannel.LEFT_ROTATING_LASERS.setSpeed(event.getValue()); diff --git a/src/main/java/exposed/hydrogen/nightclub/commands/LightCommand.java b/src/main/java/exposed/hydrogen/nightclub/commands/LightCommand.java index e5936ab..ea894d7 100644 --- a/src/main/java/exposed/hydrogen/nightclub/commands/LightCommand.java +++ b/src/main/java/exposed/hydrogen/nightclub/commands/LightCommand.java @@ -3,7 +3,13 @@ import co.aikar.commands.BaseCommand; import co.aikar.commands.annotation.*; import exposed.hydrogen.nightclub.Nightclub; -import exposed.hydrogen.nightclub.light.*; +import exposed.hydrogen.nightclub.light.Light; +import exposed.hydrogen.nightclub.light.LightUniverse; +import exposed.hydrogen.nightclub.light.LightUniverseManager; +import exposed.hydrogen.nightclub.light.data.LightData; +import exposed.hydrogen.nightclub.light.data.LightPattern; +import exposed.hydrogen.nightclub.light.data.LightPatternData; +import exposed.hydrogen.nightclub.light.data.LightType; import exposed.hydrogen.nightclub.light.event.LightChannel; import exposed.hydrogen.nightclub.light.event.LightSpeedChannel; import exposed.hydrogen.nightclub.util.Location; @@ -548,8 +554,81 @@ public static void onRemoveLightID(CommandSender sender, String[] args) { light.on(new Color(0x0066ff)); } } + + @Subcommand("ringzoom") + @CommandAlias("rz") + @Description("Modify a Light's ring zoom movement") + @CommandPermission("nightclub.light") + public class LightRingMoveCommand extends BaseCommand { + + @Subcommand("setlocation") + @CommandAlias("sl") + @Description("Set the pitch and yaw") + @CommandPermission("nightclub.light") + public static void onSetLocation(CommandSender sender, Player player, String[] args) { + List errors = isUnloaded(); + errors.add(player == null ? CommandError.COMMAND_SENT_FROM_CONSOLE : CommandError.VALID); + errors.add(!(args.length == 2) ? CommandError.TOO_LITTLE_ARGUMENTS : CommandError.VALID); + if (errors.stream().anyMatch(error -> error != CommandError.VALID)) { + sender.sendMessage(Util.formatErrors(errors)); + return; + } + if (args.length >= 2) { + try { + light.getData().getRingMovementData().setPitchYaw(new Location(0, 0, 0, Util.parseNumber(args[0]), Util.parseNumber(args[1]))); // pitch and yaw + } catch (ParseException e) { + errors.add(CommandError.INVALID_ARGUMENT); + sender.sendMessage(Util.formatErrors(errors)); + return; + } + } else { + light.getData().getRingMovementData().setPitchYaw(Location.getFromBukkitLocation(player.getLocation().add(0, 1, 0))); + } + light.buildLasers(); + light.on(new Color(0x000000)); + } + + @Subcommand("distance") + @CommandAlias("d") + @Description("Alter distance") + @CommandPermission("nightclub.light") + public static void onModifyDistance(CommandSender sender, String[] args) { + List errors = isUnloaded(args, 1); + try { + light.getData().getRingMovementData().setDistance(Util.parseNumber(args[0]).doubleValue()); + } catch (ParseException e) { + errors.add(CommandError.INVALID_ARGUMENT); + } + if (errors.stream().anyMatch(error -> error != CommandError.VALID)) { + sender.sendMessage(Util.formatErrors(errors)); + return; + } + light.buildLasers(); + light.on(new Color(0x0066ff)); + } + + @Subcommand("time") + @CommandAlias("t") + @Description("Alter time to move") + @CommandPermission("nightclub.light") + public static void onModifyTime(CommandSender sender, String[] args) { + List errors = isUnloaded(args, 1); + try { + light.getData().getRingMovementData().setDuration(Util.parseNumber(args[0]).doubleValue()); + } catch (ParseException e) { + errors.add(CommandError.INVALID_ARGUMENT); + } + if (errors.stream().anyMatch(error -> error != CommandError.VALID)) { + sender.sendMessage(Util.formatErrors(errors)); + return; + } + light.buildLasers(); + light.on(new Color(0x0066ff)); + } + } } + @Subcommand("control") @CommandAlias("c") @Description("Control a light, for example, turn it on or off") @@ -599,6 +678,5 @@ public static void onFlashOff(CommandSender sender) { } light.flashOff(new Color(0x0066ff)); } - } } \ No newline at end of file diff --git a/src/main/java/exposed/hydrogen/nightclub/commands/LightUniverseCommand.java b/src/main/java/exposed/hydrogen/nightclub/commands/LightUniverseCommand.java index 4b6861e..3379bbe 100644 --- a/src/main/java/exposed/hydrogen/nightclub/commands/LightUniverseCommand.java +++ b/src/main/java/exposed/hydrogen/nightclub/commands/LightUniverseCommand.java @@ -3,6 +3,7 @@ import co.aikar.commands.BaseCommand; import co.aikar.commands.annotation.*; import exposed.hydrogen.nightclub.Nightclub; +import exposed.hydrogen.nightclub.light.Light; import exposed.hydrogen.nightclub.light.LightUniverse; import exposed.hydrogen.nightclub.light.LightUniverseManager; import exposed.hydrogen.nightclub.light.event.LightChannel; @@ -215,5 +216,24 @@ public static void onFlashOff(CommandSender sender, String[] args) { sender.sendMessage(Util.formatErrors(errors)); } } + + @Subcommand("ringzoom") + @Description("rz") + @CommandPermission("nightclub.lightuniverse") + public static void onRingZoom(CommandSender sender) { + List errors = isUnloaded(); + try { + Nightclub.getLightUniverseManager().getLoadedUniverse().getLights().forEach(Light::ringZoom); + } catch (IllegalArgumentException e) { + errors.add(CommandError.INVALID_ARGUMENT); + } catch (ArrayIndexOutOfBoundsException e) { + errors.add(CommandError.TOO_LITTLE_ARGUMENTS); + } + if (errors.stream().anyMatch(error -> error != CommandError.VALID)) { + sender.sendMessage(Util.formatErrors(errors)); + } + } + + } } diff --git a/src/main/java/exposed/hydrogen/nightclub/laser/Laser.java b/src/main/java/exposed/hydrogen/nightclub/laser/Laser.java index 3d3bdc8..9b54f1a 100644 --- a/src/main/java/exposed/hydrogen/nightclub/laser/Laser.java +++ b/src/main/java/exposed/hydrogen/nightclub/laser/Laser.java @@ -9,6 +9,7 @@ import org.bukkit.plugin.Plugin; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitTask; +import org.bukkit.util.Vector; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -26,11 +27,11 @@ /** * A whole class to create Guardian Lasers and Ender Crystal Beams using packets and reflection.
* Inspired by the API GuardianBeamAPI
- * 1.9 -> 1.18.1 + * 1.9 -> 1.18.2 * * @author SkytAsul - * @version 2.2.1 - * @see GitHub page + * @version 2.2.4 + * @see GitHub repository */ public abstract class Laser { @@ -85,7 +86,9 @@ public Laser durationInTicks() { /** * Starts this laser. + *

* It will make the laser visible for nearby players and start the countdown to the final duration. + *

* Once finished, it will destroy the laser and execute all runnables passed with {@link Laser#executeEnd}. * * @param plugin plugin used to start the task @@ -139,7 +142,7 @@ public synchronized void cancel() throws IllegalStateException { } /** - * Stops this laser.
+ * Stops this laser. *

* This will destroy the laser for every player and start execute all runnables passed with {@link Laser#executeEnd} */ @@ -148,10 +151,21 @@ public void stop() { main.cancel(); } + /** + * Gets laser status. + * + * @return true if the laser is currently running + * (i.e. {@link #start} has been called and the duration is not over) + */ public boolean isStarted() { return main != null; } + /** + * Gets laser type. + * + * @return LaserType enum constant of this laser + */ public abstract LaserType getLaserType(); /** @@ -170,10 +184,20 @@ public boolean isStarted() { */ public abstract void moveEnd(Location location) throws ReflectiveOperationException; + /** + * Gets the start location of the laser. + * + * @return where exactly is the start position of the laser located + */ public Location getStart() { return start; } + /** + * Gets the end location of the laser. + * + * @return where exactly is the end position of the laser located + */ public Location getEnd() { return end; } @@ -229,11 +253,13 @@ public void run() { } protected void moveFakeEntity(Location location, int entityId, Object fakeEntity) throws ReflectiveOperationException { + if (fakeEntity != null) Packets.moveFakeEntity(fakeEntity, location); + if (main == null) return; + Object packet; if (fakeEntity == null) { packet = Packets.createPacketMoveEntity(location, entityId); } else { - Packets.moveFakeEntity(fakeEntity, location); packet = Packets.createPacketMoveEntity(fakeEntity); } for (Player p : show) { @@ -257,8 +283,8 @@ public static class GuardianLaser extends Laser { private Object createGuardianPacket; private Object createSquidPacket; - private final Object teamCreatePacket; - private final Object[] destroyPackets; + private Object teamCreatePacket; + private Object[] destroyPackets; private Object metadataPacketGuardian; private Object metadataPacketSquid; private Object fakeGuardianDataWatcher; @@ -275,6 +301,9 @@ public static class GuardianLaser extends Laser { protected LivingEntity endEntity; + private Location correctStart; + private Location correctEnd; + /** * Creates a new Guardian Laser instance * @@ -283,8 +312,10 @@ public static class GuardianLaser extends Laser { * @param duration Duration of laser in seconds (-1 if infinite) * @param distance Distance where laser will be visible (-1 if infinite) * @throws ReflectiveOperationException if a reflection exception occurred during Laser creation + * @see Laser#start(Plugin) to start the laser * @see #durationInTicks() to make the duration in ticks * @see #executeEnd(Runnable) to add Runnable-s to execute when the laser will stop + * @see #GuardianLaser(Location, LivingEntity, int, int) to create a laser which follows an entity */ public GuardianLaser(Location start, Location end, int duration, int distance) throws ReflectiveOperationException { super(start, end, duration, distance); @@ -294,9 +325,7 @@ public GuardianLaser(Location start, Location end, int duration, int distance) t targetID = squidID; targetUUID = squidUUID; - initGuardian(); - teamCreatePacket = Packets.createPacketTeamCreate("noclip" + teamID.getAndIncrement(), squidUUID, guardianUUID); - destroyPackets = Packets.createPacketsRemoveEntities(squidID, guardianID); + initLaser(); } /** @@ -307,8 +336,10 @@ public GuardianLaser(Location start, Location end, int duration, int distance) t * @param duration Duration of laser in seconds (-1 if infinite) * @param distance Distance where laser will be visible (-1 if infinite) * @throws ReflectiveOperationException if a reflection exception occurred during Laser creation + * @see Laser#start(Plugin) to start the laser * @see #durationInTicks() to make the duration in ticks * @see #executeEnd(Runnable) to add Runnable-s to execute when the laser will stop + * @see #GuardianLaser(Location, Location, int, int) to create a laser with a specific end location */ public GuardianLaser(Location start, LivingEntity endEntity, int duration, int distance) throws ReflectiveOperationException { super(start, endEntity.getLocation(), duration, distance); @@ -316,41 +347,65 @@ public GuardianLaser(Location start, LivingEntity endEntity, int duration, int d targetID = endEntity.getEntityId(); targetUUID = endEntity.getUniqueId(); - initGuardian(); - teamCreatePacket = Packets.createPacketTeamCreate("noclip" + teamID.getAndIncrement(), squidUUID, guardianUUID); - destroyPackets = Packets.createPacketsRemoveEntities(squidID, guardianID); + initLaser(); } - private void initGuardian() throws ReflectiveOperationException { + private void initLaser() throws ReflectiveOperationException { fakeGuardianDataWatcher = Packets.createFakeDataWatcher(); Packets.initGuardianWatcher(fakeGuardianDataWatcher, targetID); - if (Packets.version < 17) { - guardian = null; - createGuardianPacket = Packets.createPacketEntitySpawnLiving(start, Packets.mappings.getGuardianID(), guardianUUID, guardianID); - } else { - guardian = Packets.createGuardian(start, guardianUUID, guardianID); - createGuardianPacket = Packets.createPacketEntitySpawnLiving(guardian); + if (Packets.version >= 17) { + guardian = Packets.createGuardian(getCorrectStart(), guardianUUID, guardianID); } metadataPacketGuardian = Packets.createPacketMetadata(guardianID, fakeGuardianDataWatcher); + + teamCreatePacket = Packets.createPacketTeamCreate("noclip" + teamID.getAndIncrement(), squidUUID, guardianUUID); + destroyPackets = Packets.createPacketsRemoveEntities(squidID, guardianID); } private void initSquid() throws ReflectiveOperationException { - if (Packets.version < 17) { - squid = null; - createSquidPacket = Packets.createPacketEntitySpawnLiving(end, Packets.mappings.getSquidID(), squidUUID, squidID); - } else { - squid = Packets.createSquid(end, squidUUID, squidID); - createSquidPacket = Packets.createPacketEntitySpawnLiving(squid); + if (Packets.version >= 17) { + squid = Packets.createSquid(getCorrectEnd(), squidUUID, squidID); } metadataPacketSquid = Packets.createPacketMetadata(squidID, Packets.fakeSquidWatcher); Packets.setDirtyWatcher(Packets.fakeSquidWatcher); } + private Object getGuardianSpawnPacket() throws ReflectiveOperationException { + if (createGuardianPacket == null) { + if (Packets.version < 17) { + createGuardianPacket = Packets.createPacketEntitySpawnLiving(getCorrectStart(), Packets.mappings.getGuardianID(), guardianUUID, guardianID); + } else { + createGuardianPacket = Packets.createPacketEntitySpawnLiving(guardian); + } + } + return createGuardianPacket; + } + + private Object getSquidSpawnPacket() throws ReflectiveOperationException { + if (createSquidPacket == null) { + if (Packets.version < 17) { + createSquidPacket = Packets.createPacketEntitySpawnLiving(getCorrectEnd(), Packets.mappings.getSquidID(), squidUUID, squidID); + } else { + createSquidPacket = Packets.createPacketEntitySpawnLiving(squid); + } + } + return createSquidPacket; + } + @Override public LaserType getLaserType() { return LaserType.GUARDIAN; } + /** + * Makes the laser follow an entity (moving end location). + *

+ * This is done client-side by making the fake guardian follow the existing entity. + * Hence, there is no consuming of server resources. + * + * @param entity living entity the laser will follow + * @throws ReflectiveOperationException if a reflection operation fails + */ public void attachEndEntity(LivingEntity entity) throws ReflectiveOperationException { if (entity.getWorld() != start.getWorld()) throw new IllegalArgumentException("Attached entity is not in the same world as the laser."); @@ -365,6 +420,7 @@ public Entity getEndEntity() { private void setTargetEntity(UUID uuid, int id) throws ReflectiveOperationException { targetUUID = uuid; targetID = id; + fakeGuardianDataWatcher = Packets.createFakeDataWatcher(); Packets.initGuardianWatcher(fakeGuardianDataWatcher, targetID); metadataPacketGuardian = Packets.createPacketMetadata(guardianID, fakeGuardianDataWatcher); @@ -378,6 +434,26 @@ public Location getEnd() { return endEntity == null ? end : endEntity.getLocation(); } + protected Location getCorrectStart() { + if (correctStart == null) { + correctStart = start.clone(); + correctStart.subtract(0, 0.5, 0); + } + return correctStart; + } + + protected Location getCorrectEnd() { + if (correctEnd == null) { + correctEnd = end.clone(); + correctEnd.subtract(0, 0.5, 0); + + Vector corrective = correctEnd.toVector().subtract(getCorrectStart().toVector()).normalize(); + correctEnd.subtract(corrective); + + } + return correctEnd; + } + @Override protected boolean isCloseEnough(Player player) { return player == endEntity || super.isCloseEnough(player); @@ -385,8 +461,18 @@ protected boolean isCloseEnough(Player player) { @Override protected void sendStartPackets(Player p, boolean hasSeen) throws ReflectiveOperationException { - Packets.sendPackets(p, createGuardianPacket, createSquidPacket); - Packets.sendPackets(p, metadataPacketGuardian, metadataPacketSquid); + if (squid == null) { + Packets.sendPackets(p, + getGuardianSpawnPacket(), + metadataPacketGuardian); + } else { + Packets.sendPackets(p, + getGuardianSpawnPacket(), + getSquidSpawnPacket(), + metadataPacketGuardian, + metadataPacketSquid); + } + if (!hasSeen) Packets.sendPackets(p, teamCreatePacket); } @@ -398,24 +484,31 @@ protected void sendDestroyPackets(Player p) throws ReflectiveOperationException @Override public void moveStart(Location location) throws ReflectiveOperationException { this.start = location; - initGuardian(); - if (main != null) { - moveFakeEntity(start, guardianID, guardian); + correctStart = null; + + createGuardianPacket = null; // will force re-generation of spawn packet + moveFakeEntity(getCorrectStart(), guardianID, guardian); + + if (squid != null) { + correctEnd = null; + createSquidPacket = null; + moveFakeEntity(getCorrectEnd(), squidID, squid); } } @Override public void moveEnd(Location location) throws ReflectiveOperationException { this.end = location; - initSquid(); - if (main != null) { - if (squid == null) { - for (Player p : show) { - Packets.sendPackets(p, createSquidPacket, metadataPacketSquid); - } - } else { - moveFakeEntity(end, squidID, squid); + createSquidPacket = null; // will force re-generation of spawn packet + correctEnd = null; + + if (squid == null) { + initSquid(); + for (Player p : show) { + Packets.sendPackets(p, getSquidSpawnPacket(), metadataPacketSquid); } + } else { + moveFakeEntity(getCorrectEnd(), squidID, squid); } if (targetUUID != squidUUID) { endEntity = null; @@ -424,7 +517,7 @@ public void moveEnd(Location location) throws ReflectiveOperationException { } /** - * Asks viewers' clients to change the color of this Laser + * Asks viewers' clients to change the color of this laser * * @throws ReflectiveOperationException */ @@ -441,10 +534,10 @@ public static class CrystalLaser extends Laser { private Object createCrystalPacket; private Object metadataPacketCrystal; private final Object[] destroyPackets; - private Object fakeCrystalDataWatcher; + private final Object fakeCrystalDataWatcher; private final Object crystal; - private final int crystalID; + private final int crystalID = Packets.generateEID(); /** * Creates a new Ender Crystal Laser instance @@ -454,6 +547,7 @@ public static class CrystalLaser extends Laser { * @param duration Duration of laser in seconds (-1 if infinite) * @param distance Distance where laser will be visible (-1 if infinite) * @throws ReflectiveOperationException if a reflection exception occurred during Laser creation + * @see #start(Plugin) to start the laser * @see #durationInTicks() to make the duration in ticks * @see #executeEnd(Runnable) to add Runnable-s to execute when the laser will stop */ @@ -464,26 +558,23 @@ public CrystalLaser(Location start, Location end, int duration, int distance) th Packets.setCrystalWatcher(fakeCrystalDataWatcher, end); if (Packets.version < 17) { crystal = null; - createCrystalPacket = Packets.createPacketEntitySpawnNormal(start, Packets.crystalID, Packets.crystalType); } else { - crystal = Packets.createCrystal(start); - createCrystalPacket = Packets.createPacketEntitySpawnNormal(crystal); + crystal = Packets.createCrystal(start, UUID.randomUUID(), crystalID); } - crystalID = (int) Packets.getField(createCrystalPacket, Packets.version < 17 ? "a" : "c"); metadataPacketCrystal = Packets.createPacketMetadata(crystalID, fakeCrystalDataWatcher); destroyPackets = Packets.createPacketsRemoveEntities(crystalID); } - protected void refreshPackets() throws ReflectiveOperationException { - fakeCrystalDataWatcher = Packets.createFakeDataWatcher(); - Packets.setCrystalWatcher(fakeCrystalDataWatcher, end); - if (Packets.version < 17) { - createCrystalPacket = Packets.createPacketEntitySpawnNormal(start, Packets.crystalID, Packets.crystalType); - } else { - createCrystalPacket = Packets.createPacketEntitySpawnNormal(crystal); + private Object getCrystalSpawnPacket() throws ReflectiveOperationException { + if (createCrystalPacket == null) { + if (Packets.version < 17) { + createCrystalPacket = Packets.createPacketEntitySpawnNormal(start, Packets.crystalID, Packets.crystalType, crystalID); + } else { + createCrystalPacket = Packets.createPacketEntitySpawnNormal(crystal); + } } - metadataPacketCrystal = Packets.createPacketMetadata(crystalID, fakeCrystalDataWatcher); + return createCrystalPacket; } @Override @@ -493,7 +584,7 @@ public LaserType getLaserType() { @Override protected void sendStartPackets(Player p, boolean hasSeen) throws ReflectiveOperationException { - Packets.sendPackets(p, createCrystalPacket); + Packets.sendPackets(p, getCrystalSpawnPacket()); Packets.sendPackets(p, metadataPacketCrystal); } @@ -505,14 +596,13 @@ protected void sendDestroyPackets(Player p) throws ReflectiveOperationException @Override public void moveStart(Location location) throws ReflectiveOperationException { this.start = location; - refreshPackets(); - if (main != null) moveFakeEntity(start, crystalID, crystal); + createCrystalPacket = null; // will force re-generation of spawn packet + moveFakeEntity(start, crystalID, crystal); } @Override public void moveEnd(Location location) throws ReflectiveOperationException { this.end = location; - refreshPackets(); if (main != null) { Packets.setCrystalWatcher(fakeCrystalDataWatcher, location); metadataPacketCrystal = Packets.createPacketMetadata(crystalID, fakeCrystalDataWatcher); @@ -525,7 +615,20 @@ public void moveEnd(Location location) throws ReflectiveOperationException { } public enum LaserType { - GUARDIAN, ENDER_CRYSTAL; + /** + * Represents a laser from a Guardian entity. + *

+ * It can be pointed to precise locations and + * can track entities smoothly using {@link GuardianLaser#attachEndEntity(LivingEntity)} + */ + GUARDIAN, + + /** + * Represents a laser from an Ender Crystal entity. + *

+ * Start and end locations are automatically rounded to integers (block locations). + */ + ENDER_CRYSTAL; /** * Creates a new Laser instance, {@link GuardianLaser} or {@link CrystalLaser} depending on this enum value. @@ -535,8 +638,9 @@ public enum LaserType { * @param duration Duration of laser in seconds (-1 if infinite) * @param distance Distance where laser will be visible * @throws ReflectiveOperationException if a reflection exception occurred during Laser creation - * @see #durationInTicks() to make the duration in ticks - * @see #executeEnd(Runnable) to add Runnable-s to execute when the laser will stop + * @see Laser#start(Plugin) to start the laser + * @see Laser#durationInTicks() to make the duration in ticks + * @see Laser#executeEnd(Runnable) to add Runnable-s to execute when the laser will stop */ public Laser create(Location start, Location end, int duration, int distance) throws ReflectiveOperationException { switch (this) { @@ -565,15 +669,19 @@ static int generateEID() { private static final int crystalID = 51; // pre-1.13 - private static Class entityTypesClass; private static Object crystalType; private static Object squidType; private static Object guardianType; + private static Constructor crystalConstructor; + private static Constructor squidConstructor; + private static Constructor guardianConstructor; + private static Object watcherObject1; // invisilibity private static Object watcherObject2; // spikes private static Object watcherObject3; // attack id private static Object watcherObject4; // crystal target + private static Object watcherObject5; // crystal base plate private static Constructor watcherConstructor; private static Method watcherSet; @@ -630,6 +738,7 @@ public void log(LogRecord logRecord) { versions = Bukkit.getBukkitVersion().split("-R")[0].split("\\."); versionMinor = versions.length <= 2 ? 0 : Integer.parseInt(versions[2]); } else versionMinor = Integer.parseInt(versions[2].substring(1)); // 1.X.Y + logger.info("Found server version 1." + version + "." + versionMinor); mappings = ProtocolMappings.getMappings(version); if (mappings == null) { @@ -638,12 +747,16 @@ public void log(LogRecord logRecord) { } logger.info("Loaded mappings " + mappings.name()); + Class entityTypesClass = getNMSClass("world.entity", "EntityTypes"); Class entityClass = getNMSClass("world.entity", "Entity"); - entityTypesClass = getNMSClass("world.entity", "EntityTypes"); + Class crystalClass = getNMSClass("world.entity.boss.enderdragon", "EntityEnderCrystal"); + Class squidClass = getNMSClass("world.entity.animal", "EntitySquid"); + Class guardianClass = getNMSClass("world.entity.monster", "EntityGuardian"); watcherObject1 = getField(entityClass, mappings.getWatcherFlags(), null); - watcherObject2 = getField(getNMSClass("world.entity.monster", "EntityGuardian"), mappings.getWatcherSpikes(), null); - watcherObject3 = getField(getNMSClass("world.entity.monster", "EntityGuardian"), mappings.getWatcherTargetEntity(), null); - watcherObject4 = getField(getNMSClass("world.entity.boss.enderdragon", "EntityEnderCrystal"), mappings.getWatcherTargetLocation(), null); + watcherObject2 = getField(guardianClass, mappings.getWatcherSpikes(), null); + watcherObject3 = getField(guardianClass, mappings.getWatcherTargetEntity(), null); + watcherObject4 = getField(crystalClass, mappings.getWatcherTargetLocation(), null); + watcherObject5 = getField(crystalClass, mappings.getWatcherBasePlate(), null); if (version >= 13) { crystalType = entityTypesClass.getDeclaredField(mappings.getCrystalTypeName()).get(null); @@ -673,8 +786,15 @@ public void log(LogRecord logRecord) { blockPositionConstructor = getNMSClass("core", "BlockPosition").getConstructor(double.class, double.class, double.class); nmsWorld = Class.forName(cpack + "CraftWorld").getDeclaredMethod("getHandle").invoke(Bukkit.getWorlds().get(0)); - Object[] entityConstructorParams = version < 14 ? new Object[]{nmsWorld} : new Object[]{getNMSClass("world.entity", "EntityTypes").getDeclaredField(version < 17 ? "SQUID" : "aJ").get(null), nmsWorld}; - fakeSquid = getNMSClass("world.entity.animal", "EntitySquid").getDeclaredConstructors()[0].newInstance(entityConstructorParams); + + squidConstructor = squidClass.getDeclaredConstructors()[0]; + if (version >= 17) { + guardianConstructor = guardianClass.getDeclaredConstructors()[0]; + crystalConstructor = crystalClass.getDeclaredConstructor(nmsWorld.getClass().getSuperclass(), double.class, double.class, double.class); + } + + Object[] entityConstructorParams = version < 14 ? new Object[]{nmsWorld} : new Object[]{entityTypesClass.getDeclaredField(version < 17 ? "SQUID" : "aJ").get(null), nmsWorld}; + fakeSquid = squidConstructor.newInstance(entityConstructorParams); fakeSquidWatcher = createFakeDataWatcher(); tryWatcherSet(fakeSquidWatcher, watcherObject1, (byte) 32); @@ -729,21 +849,23 @@ public static void setDirtyWatcher(Object watcher) throws ReflectiveOperationExc } public static Object createSquid(Location location, UUID uuid, int id) throws ReflectiveOperationException { - Object entity = getNMSClass("world.entity.animal", "EntitySquid").getDeclaredConstructors()[0].newInstance(squidType, nmsWorld); + Object entity = squidConstructor.newInstance(squidType, nmsWorld); setEntityIDs(entity, uuid, id); moveFakeEntity(entity, location); return entity; } public static Object createGuardian(Location location, UUID uuid, int id) throws ReflectiveOperationException { - Object entity = getNMSClass("world.entity.monster", "EntityGuardian").getDeclaredConstructors()[0].newInstance(guardianType, nmsWorld); + Object entity = guardianConstructor.newInstance(guardianType, nmsWorld); setEntityIDs(entity, uuid, id); moveFakeEntity(entity, location); return entity; } - public static Object createCrystal(Location location) throws ReflectiveOperationException { - return getNMSClass("world.entity.boss.enderdragon", "EntityEnderCrystal").getDeclaredConstructor(nmsWorld.getClass().getSuperclass(), double.class, double.class, double.class).newInstance(nmsWorld, location.getX(), location.getY(), location.getZ()); + public static Object createCrystal(Location location, UUID uuid, int id) throws ReflectiveOperationException { + Object entity = crystalConstructor.newInstance(nmsWorld, location.getX(), location.getY(), location.getZ()); + setEntityIDs(entity, uuid, id); + return entity; } public static Object createPacketEntitySpawnLiving(Location location, int typeID, UUID uuid, int id) throws ReflectiveOperationException { @@ -760,9 +882,9 @@ public static Object createPacketEntitySpawnLiving(Location location, int typeID return packet; } - public static Object createPacketEntitySpawnNormal(Location location, int typeID, Object type) throws ReflectiveOperationException { + public static Object createPacketEntitySpawnNormal(Location location, int typeID, Object type, int id) throws ReflectiveOperationException { Object packet = packetSpawnNormal.newInstance(); - setField(packet, "a", generateEID()); + setField(packet, "a", id); setField(packet, "b", UUID.randomUUID()); setField(packet, "c", location.getX()); setField(packet, "d", location.getY()); @@ -783,13 +905,14 @@ public static Object createPacketEntitySpawnNormal(Object entity) throws Reflect public static void initGuardianWatcher(Object watcher, int targetId) throws ReflectiveOperationException { tryWatcherSet(watcher, watcherObject1, (byte) 32); - tryWatcherSet(watcher, watcherObject2, false); + tryWatcherSet(watcher, watcherObject2, Boolean.FALSE); tryWatcherSet(watcher, watcherObject3, targetId); } public static void setCrystalWatcher(Object watcher, Location target) throws ReflectiveOperationException { Object blockPosition = blockPositionConstructor.newInstance(target.getX(), target.getY(), target.getZ()); tryWatcherSet(watcher, watcherObject4, version < 13 ? com.google.common.base.Optional.of(blockPosition) : Optional.of(blockPosition)); + tryWatcherSet(watcher, watcherObject5, Boolean.FALSE); } public static Object[] createPacketsRemoveEntities(int... entitiesId) throws ReflectiveOperationException { @@ -883,12 +1006,6 @@ private static Object getField(Class clazz, String name, Object instance) thr return field.get(instance); } - private static Object getField(Object instance, String name) throws ReflectiveOperationException { - Field field = instance.getClass().getDeclaredField(name); - field.setAccessible(true); - return field.get(instance); - } - private static Class getNMSClass(String package17, String className) throws ClassNotFoundException { return Class.forName((version < 17 ? npack : "net.minecraft." + package17) + "." + className); } @@ -899,27 +1016,32 @@ public interface ReflectiveConsumer { void accept(T t) throws ReflectiveOperationException; } - enum ProtocolMappings { - V1_9(9, "Z", "bA", "bB", "b", 94, 68), + private enum ProtocolMappings { + V1_9(9, "Z", "bA", "bB", "b", "c", 94, 68), V1_10(10, V1_9), V1_11(11, V1_10), V1_12(12, V1_11), - V1_13(13, "ac", "bF", "bG", "b", 70, 28), - V1_14(14, "W", "b", "bD", "c", 73, 30), - V1_15(15, "T", "b", "bA", "c", 74, 31), - V1_16(16, "T", "b", "d", "c", 74, 31, "u", null, null) { + V1_13(13, "ac", "bF", "bG", "b", "c", 70, 28), + V1_14(14, "W", "b", "bD", "c", "d", 73, 30), + V1_15(15, "T", "b", "bA", "c", "d", 74, 31), + V1_16(16, null, "b", "d", "c", "d", -1, 31, "u", null, null) { @Override public int getSquidID() { - return Packets.versionMinor < 2 ? super.getSquidID() : 81; + return Packets.versionMinor < 2 ? 74 : 81; } @Override public String getWatcherFlags() { - return Packets.versionMinor < 2 ? super.getWatcherFlags() : "S"; + return Packets.versionMinor < 2 ? "T" : "S"; + } + }, + V1_17(17, "Z", "b", "e", "c", "d", 86, 35, "u", "setCollisionRule", "getPlayerNameSet"), + V1_18(18, null, "b", "e", "c", "d", 86, 35, "u", "a", "g") { + @Override + public String getWatcherFlags() { + return Packets.versionMinor < 2 ? "aa" : "Z"; } }, - V1_17(17, "Z", "b", "e", "c", 86, 35, "u", "setCollisionRule", "getPlayerNameSet"), - V1_18(18, "aa", "b", "e", "c", 86, 35, "u", "a", "g"), ; private final int major; @@ -927,6 +1049,7 @@ public String getWatcherFlags() { private final String watcherSpikes; private final String watcherTargetEntity; private final String watcherTargetLocation; + private final String watcherBasePlate; private final int squidID; private final int guardianID; private final String crystalTypeName; @@ -934,17 +1057,17 @@ public String getWatcherFlags() { private final String teamGetPlayers; ProtocolMappings(int major, ProtocolMappings parent) { - this(major, parent.watcherFlags, parent.watcherSpikes, parent.watcherTargetEntity, parent.watcherTargetLocation, parent.squidID, parent.guardianID, parent.crystalTypeName, parent.teamSetCollision, parent.teamGetPlayers); + this(major, parent.watcherFlags, parent.watcherSpikes, parent.watcherTargetEntity, parent.watcherTargetLocation, parent.watcherBasePlate, parent.squidID, parent.guardianID, parent.crystalTypeName, parent.teamSetCollision, parent.teamGetPlayers); } ProtocolMappings(int major, - String watcherFlags, String watcherSpikes, String watcherTargetEntity, String watcherTargetLocation, + String watcherFlags, String watcherSpikes, String watcherTargetEntity, String watcherTargetLocation, String watcherBasePlate, int squidID, int guardianID) { - this(major, watcherFlags, watcherSpikes, watcherTargetEntity, watcherTargetLocation, squidID, guardianID, "END_CRYSTAL", null, null); + this(major, watcherFlags, watcherSpikes, watcherTargetEntity, watcherTargetLocation, watcherBasePlate, squidID, guardianID, "END_CRYSTAL", null, null); } ProtocolMappings(int major, - String watcherFlags, String watcherSpikes, String watcherTargetEntity, String watcherTargetLocation, + String watcherFlags, String watcherSpikes, String watcherTargetEntity, String watcherTargetLocation, String watcherBasePlate, int squidID, int guardianID, String crystalTypeName, String teamSetCollision, String teamGetPlayers) { this.major = major; @@ -952,6 +1075,7 @@ public String getWatcherFlags() { this.watcherSpikes = watcherSpikes; this.watcherTargetEntity = watcherTargetEntity; this.watcherTargetLocation = watcherTargetLocation; + this.watcherBasePlate = watcherBasePlate; this.squidID = squidID; this.guardianID = guardianID; this.crystalTypeName = crystalTypeName; @@ -979,6 +1103,10 @@ public String getWatcherTargetLocation() { return watcherTargetLocation; } + public String getWatcherBasePlate() { + return watcherBasePlate; + } + public int getSquidID() { return squidID; } diff --git a/src/main/java/exposed/hydrogen/nightclub/laser/LaserWrapper.java b/src/main/java/exposed/hydrogen/nightclub/laser/LaserWrapper.java index 75123b4..69df592 100644 --- a/src/main/java/exposed/hydrogen/nightclub/laser/LaserWrapper.java +++ b/src/main/java/exposed/hydrogen/nightclub/laser/LaserWrapper.java @@ -1,7 +1,7 @@ package exposed.hydrogen.nightclub.laser; import exposed.hydrogen.nightclub.Nightclub; -import exposed.hydrogen.nightclub.light.LightType; +import exposed.hydrogen.nightclub.light.data.LightType; import exposed.hydrogen.nightclub.util.Location; import lombok.Getter; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/exposed/hydrogen/nightclub/light/Light.java b/src/main/java/exposed/hydrogen/nightclub/light/Light.java index da15fc9..abcee0c 100644 --- a/src/main/java/exposed/hydrogen/nightclub/light/Light.java +++ b/src/main/java/exposed/hydrogen/nightclub/light/Light.java @@ -3,6 +3,7 @@ import com.google.gson.InstanceCreator; import com.google.gson.JsonArray; import exposed.hydrogen.nightclub.laser.LaserWrapper; +import exposed.hydrogen.nightclub.light.data.*; import exposed.hydrogen.nightclub.light.event.LightChannel; import exposed.hydrogen.nightclub.light.event.LightSpeedChannel; import exposed.hydrogen.nightclub.util.DebugMarker; @@ -34,7 +35,7 @@ public class Light implements LightI { // annotations lol @Getter private final UUID uniqueID; @Getter @Setter private String name; - @Getter @Setter private Location location; + @Getter private Location location; // only changes when manually set @Getter @Setter private LightData data; @Getter private LightType type; @Getter private LightChannel channel; @@ -42,15 +43,18 @@ public class Light implements LightI { @Getter private transient final DebugMarker marker; private final transient List lasers = new ArrayList<>(); - @Getter @Setter private transient double length = 0; // 0 to 100, percentage of maxLength. + private transient double length = 0; // 0 to 100, percentage of maxLength. + private transient double zoomTime = 0; // 0 to 1, current zoom time. does nothing if <=0 @Getter @Setter private transient double x = 0; // 0 to 100, usually percentage of 360 @Getter @Setter private transient double x2 = 0; // 0 to 100, usually percentage of 360, secondary pattern private transient boolean isOn = false; - @Getter @Setter private transient double multipliedSpeed; // speed, but when internally multiplied by events - @Getter @Setter private transient double secondaryMultipliedSpeed; - @Getter @Setter private transient int timeToFade; // internal fade off value + private transient boolean isZoomed = false; + private transient double multipliedSpeed; // speed, but when internally multiplied by events + private transient double secondaryMultipliedSpeed; + private transient int timeToFade; // internal fade off value private final transient Runnable run; private transient boolean isLoaded; + private transient Location loc; // runtime location, may change public Light(Location loc, LightPattern pattern, LightType type, LightChannel channel) { this(loc, "Unnamed-Light" + new Random().nextInt(), pattern, type, channel); @@ -63,7 +67,7 @@ public Light(Location loc, String name, LightPattern pattern, LightType type, Li public Light(Location loc, UUID uniqueID, String name, LightPattern pattern, LightType type, LightChannel channel) { this(uniqueID, name, loc, type, channel, LightSpeedChannel.DEFAULT, new LightData( new LightPatternData(pattern, 0, 0, 0, 0), new LightPatternData(LightPattern.STILL, - 0, 0, 0, 0), new ArrayList<>(), 0, 0, 0, + 0, 0, 0, 0), new ArrayList<>(), new RingMovementData(new Location(), 0, 0), 0, 0, 0, 0, false)); } @@ -78,14 +82,22 @@ public Light(UUID uuid, String name, Location location, LightType type, LightCha this.data = data; this.marker = new DebugMarker(location.getBukkitLocation(), new Color(0, 0, 0), "", 5000); + loc = this.location; + if (this.data.isFlipStartAndEnd()) { - lasers.forEach((laser) -> laser.setEnd(this.location)); + lasers.forEach((laser) -> laser.setEnd(this.loc)); } else { - lasers.forEach((laser) -> laser.setStart(this.location)); + lasers.forEach((laser) -> laser.setStart(this.loc)); } run = () -> { try { + if (isZoomed ? zoomTime > 0 : zoomTime < 1) { + double duration = getData().getRingMovementData().getDuration(); + zoomTime = isZoomed ? + zoomTime - duration >= 0 ? duration / 1000 / (DELAY / 10.0) : 1 + : zoomTime + duration <= 1 ? duration / 1000 / (DELAY / 10.0) : 1; + } if (timeToFade > 0 && length > 0) { timeToFade--; length -= 100.0 / this.data.getTimeToFadeToBlack(); @@ -106,11 +118,12 @@ public Light(UUID uuid, String name, Location location, LightType type, LightCha /* Here we make a ray the size of (length) from the location of this Light, then we add a 2d plane to it (which is where our pattern is) with an x value that is separated evenly for each laser. This pattern is then moved (as a whole) by the second pattern. + The pattern should also curve. */ // x position evenly separated for each laser double separated = x + (100.0 / lasers.size()) * i; // a (invisible) "ray" the size of length, pointing towards the set pitch and yaw - Vector3D v = new Vector3D(Math.toRadians(this.location.getYaw()), Math.toRadians(this.location.getPitch())).normalize().scalarMultiply(getMaxLengthPercent()); + Vector3D v = new Vector3D(Math.toRadians(this.loc.getYaw()), Math.toRadians(this.loc.getPitch())).normalize().scalarMultiply(getMaxLengthPercent()); // rotation for first and second patterns, determines "start position" when pattern is a circle Rotation r = new Rotation(v, this.data.getPatternData().getRotation(), RotationConvention.FRAME_TRANSFORM); Rotation r2 = new Rotation(v, this.data.getSecondPatternData().getRotation(), RotationConvention.FRAME_TRANSFORM); @@ -120,10 +133,13 @@ x value that is separated evenly for each laser. This pattern is then moved (as Vector3D v3 = this.data.getSecondPatternData().getPattern().apply(v, x2, r2, this.data.getSecondPatternData().getPatternSizeMultiplier() * (length / 100)); Vector3D v4 = v.add(v3).add(v2); + Location ringZoomMove = getData().getRingMovementData().calculateMovement(zoomTime); if (this.data.isFlipStartAndEnd()) { - laser.setStart(this.location.clone().add(v4.getX(), v4.getZ(), v4.getY())); + laser.setStart(this.loc.clone().add(v4.getX(), v4.getZ(), v4.getY()).add(ringZoomMove)); + laser.setEnd(this.loc.clone().add(ringZoomMove)); } else { - laser.setEnd(this.location.clone().add(v4.getX(), v4.getZ(), v4.getY())); + laser.setEnd(this.loc.clone().add(v4.getX(), v4.getZ(), v4.getY()).add(ringZoomMove)); + laser.setStart(this.loc.clone().add(ringZoomMove)); } } } catch (Exception e) { @@ -185,16 +201,16 @@ public void buildLasers() { for (int i = 0; i < data.getLightCount(); i++) { LaserWrapper laser; double separated = 0 + (100.0 / data.getLightCount()) * i; - Vector3D v = new Vector3D(Math.toRadians(this.location.getYaw()), Math.toRadians(this.location.getPitch())).normalize().scalarMultiply(data.getMaxLength() * data.getOnLength() / 100.0); + Vector3D v = new Vector3D(Math.toRadians(this.loc.getYaw()), Math.toRadians(this.loc.getPitch())).normalize().scalarMultiply(data.getMaxLength() * data.getOnLength() / 100.0); Rotation r = new Rotation(v, this.data.getPatternData().getRotation(), RotationConvention.FRAME_TRANSFORM); Rotation r2 = new Rotation(v, this.data.getSecondPatternData().getRotation(), RotationConvention.FRAME_TRANSFORM); Vector3D v2 = data.getPatternData().getPattern().apply(v, separated, r, data.getPatternData().getPatternSizeMultiplier() * (data.getOnLength() / 100)); Vector3D v3 = data.getSecondPatternData().getPattern().apply(v, 0.0, r2, data.getSecondPatternData().getPatternSizeMultiplier() * (data.getOnLength() / 100)); Vector3D v4 = v.add(v3).add(v2); if (data.isFlipStartAndEnd()) { - laser = new LaserWrapper(location.clone().add(v4.getX(), v4.getZ(), v4.getY()), location, -1, 256, type); + laser = new LaserWrapper(loc.clone().add(v4.getX(), v4.getZ(), v4.getY()), loc, -1, 256, type); } else { - laser = new LaserWrapper(location, location.clone().add(v4.getX(), v4.getZ(), v4.getY()), -1, 256, type); + laser = new LaserWrapper(loc, loc.clone().add(v4.getX(), v4.getZ(), v4.getY()), -1, 256, type); } lasers.add(laser); } @@ -215,7 +231,7 @@ public void on(Color color, @Nullable JsonArray lightIDs) { length = Math.max(data.getOnLength() * (color.getAlpha() / 255.0), 0.05); isOn = true; timeToFade = 0; - marker.setLocation(location.getBukkitLocation()); + marker.setLocation(loc.getBukkitLocation()); marker.setColor(color); marker.start(256); } @@ -255,7 +271,7 @@ public void flash(Color color, @Nullable JsonArray lightIDs) { length += (100 - data.getOnLength()) / 3; timeToFade += 3; lasers.forEach(LaserWrapper::changeColor); - marker.setLocation(location.getBukkitLocation()); + marker.setLocation(loc.getBukkitLocation()); marker.setColor(color); marker.start(256); } else { @@ -277,7 +293,7 @@ public void flashOff(Color color, @Nullable JsonArray lightIDs) { return; on(color); flash(color); - marker.setLocation(location.getBukkitLocation()); + marker.setLocation(loc.getBukkitLocation()); marker.setColor(color); marker.start(256); timeToFade = data.getTimeToFadeToBlack(); @@ -287,6 +303,19 @@ public void flashOff(Color color) { flashOff(color, null); } + public void ringZoom() { + if (!isLoaded) return; + isZoomed = !isZoomed; + zoomTime = isZoomed ? 0 : 1; + marker.setLocation(loc.getBukkitLocation()); + marker.start(256); + } + + public void setLocation(Location location) { + this.loc = location; + this.location = location; + } + /** * Set which LightChannel this Light should be listening to. * @@ -361,22 +390,18 @@ public void setType(LightType type) { public String toString() { return """ - uniqueID="this.uniqueID - ", name="this.name - ", location="this.location + name="this.name ", channel="this.channel ", speedChannel="this.speedChannel ", lightIDs="lightid """ - .replace("this.uniqueID", this.uniqueID.toString()) .replace("this.name", "" + this.name) - .replace("this.location", "" + this.location) .replace("this.channel", "" + this.channel) .replace("this.speedChannel", "" + this.speedChannel) .replace("lightid", "" + data.getLightIDs()); } - public static class LightUniverseInstanceCreator implements InstanceCreator { + public static class LightInstanceCreator implements InstanceCreator { public Light createInstance(Type type) { return new Light(new Location(0, 0, 0, 0, 0), LightPattern.STILL, LightType.GUARDIAN_BEAM, LightChannel.CENTER_LIGHTS); } diff --git a/src/main/java/exposed/hydrogen/nightclub/light/LightUniverse.java b/src/main/java/exposed/hydrogen/nightclub/light/LightUniverse.java index 87e86b9..81ad0b7 100644 --- a/src/main/java/exposed/hydrogen/nightclub/light/LightUniverse.java +++ b/src/main/java/exposed/hydrogen/nightclub/light/LightUniverse.java @@ -91,5 +91,6 @@ public LightUniverse createInstance(Type type) { return new LightUniverse(new ArrayList<>(), UUID.randomUUID(), 0, "LightUniverseInstanceCreator"); } } + } diff --git a/src/main/java/exposed/hydrogen/nightclub/light/LightData.java b/src/main/java/exposed/hydrogen/nightclub/light/data/LightData.java similarity index 54% rename from src/main/java/exposed/hydrogen/nightclub/light/LightData.java rename to src/main/java/exposed/hydrogen/nightclub/light/data/LightData.java index e93a1a7..c1762c8 100644 --- a/src/main/java/exposed/hydrogen/nightclub/light/LightData.java +++ b/src/main/java/exposed/hydrogen/nightclub/light/data/LightData.java @@ -1,9 +1,12 @@ -package exposed.hydrogen.nightclub.light; +package exposed.hydrogen.nightclub.light.data; +import com.google.gson.InstanceCreator; +import exposed.hydrogen.nightclub.util.Location; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import java.lang.reflect.Type; import java.util.ArrayList; @Data @@ -13,6 +16,7 @@ public class LightData implements Cloneable { private LightPatternData patternData; private LightPatternData secondPatternData; private ArrayList lightIDs; + private RingMovementData ringMovementData; private double maxLength; private double onLength; // 0 to 100, percentage of maxLength private int timeToFadeToBlack; // x * 100 ms @@ -23,10 +27,18 @@ public class LightData implements Cloneable { @Override public LightData clone() { try { - LightData clone = (LightData) super.clone(); - return clone; + return (LightData) super.clone(); } catch (CloneNotSupportedException e) { throw new AssertionError(); } } + + public static class LightDataInstanceCreator implements InstanceCreator { + public LightData createInstance(Type type) { + return new LightData( + new LightPatternData(LightPattern.CIRCLE, 0, 0, 0, 0), new LightPatternData(LightPattern.STILL, + 0, 0, 0, 0), new ArrayList<>(), new RingMovementData(new Location(), 1, 1), 0, 0, 0, + 0, false); + } + } } diff --git a/src/main/java/exposed/hydrogen/nightclub/light/LightPattern.java b/src/main/java/exposed/hydrogen/nightclub/light/data/LightPattern.java similarity index 92% rename from src/main/java/exposed/hydrogen/nightclub/light/LightPattern.java rename to src/main/java/exposed/hydrogen/nightclub/light/data/LightPattern.java index 16d9b72..1b30b69 100644 --- a/src/main/java/exposed/hydrogen/nightclub/light/LightPattern.java +++ b/src/main/java/exposed/hydrogen/nightclub/light/data/LightPattern.java @@ -1,4 +1,4 @@ -package exposed.hydrogen.nightclub.light; +package exposed.hydrogen.nightclub.light.data; import exposed.hydrogen.nightclub.util.Util; import org.apache.commons.math3.geometry.euclidean.threed.Plane; @@ -19,7 +19,7 @@ public enum LightPattern { Math.cos(Math.toRadians(Util.getDegreesFromPercentage(x))))), STILL((x) -> new Vector2D(0.0, 0.0).scalarMultiply(0.0)); - Function pattern; + private final Function pattern; LightPattern(Function pattern) { this.pattern = pattern; diff --git a/src/main/java/exposed/hydrogen/nightclub/light/LightPatternData.java b/src/main/java/exposed/hydrogen/nightclub/light/data/LightPatternData.java similarity index 85% rename from src/main/java/exposed/hydrogen/nightclub/light/LightPatternData.java rename to src/main/java/exposed/hydrogen/nightclub/light/data/LightPatternData.java index 0fcea1a..50fad4a 100644 --- a/src/main/java/exposed/hydrogen/nightclub/light/LightPatternData.java +++ b/src/main/java/exposed/hydrogen/nightclub/light/data/LightPatternData.java @@ -1,4 +1,4 @@ -package exposed.hydrogen.nightclub.light; +package exposed.hydrogen.nightclub.light.data; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/src/main/java/exposed/hydrogen/nightclub/light/LightType.java b/src/main/java/exposed/hydrogen/nightclub/light/data/LightType.java similarity index 87% rename from src/main/java/exposed/hydrogen/nightclub/light/LightType.java rename to src/main/java/exposed/hydrogen/nightclub/light/data/LightType.java index a68057c..ed25daa 100644 --- a/src/main/java/exposed/hydrogen/nightclub/light/LightType.java +++ b/src/main/java/exposed/hydrogen/nightclub/light/data/LightType.java @@ -1,4 +1,4 @@ -package exposed.hydrogen.nightclub.light; +package exposed.hydrogen.nightclub.light.data; import exposed.hydrogen.nightclub.laser.Laser; import lombok.Getter; diff --git a/src/main/java/exposed/hydrogen/nightclub/light/data/RingMovementData.java b/src/main/java/exposed/hydrogen/nightclub/light/data/RingMovementData.java new file mode 100644 index 0000000..9a2fab0 --- /dev/null +++ b/src/main/java/exposed/hydrogen/nightclub/light/data/RingMovementData.java @@ -0,0 +1,28 @@ +package exposed.hydrogen.nightclub.light.data; + +import exposed.hydrogen.nightclub.util.Location; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; + +@Data +@AllArgsConstructor +public class RingMovementData { + private Location pitchYaw; + private double distance; + /** + * How long the ring movement should take, in milliseconds. + */ + private double duration; + + /** + * Calculates movement based on duration and distance, uses set Location as the pitch and yaw to move to + * + * @param time current time, 0-1 + * @return Location to move to + */ + public Location calculateMovement(double time) { + Vector3D v = new Vector3D(Math.toRadians(pitchYaw.getYaw()), Math.toRadians(pitchYaw.getPitch())).normalize().scalarMultiply(distance * time); + return Location.fromVector3D(v); + } +} diff --git a/src/main/java/exposed/hydrogen/nightclub/util/Location.java b/src/main/java/exposed/hydrogen/nightclub/util/Location.java index 0b71954..c590476 100644 --- a/src/main/java/exposed/hydrogen/nightclub/util/Location.java +++ b/src/main/java/exposed/hydrogen/nightclub/util/Location.java @@ -1,6 +1,7 @@ package exposed.hydrogen.nightclub.util; import lombok.Data; +import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; import org.bukkit.Bukkit; // yet another preparation for minestom support, not much reason to use otherwise, but this does also translate @@ -18,7 +19,7 @@ public class Location implements Cloneable { private double yaw; /** - * GSON no-args constructor + * No-args constructor, all values are set to 0 */ public Location() { this(0, 0, 0, 0, 0); @@ -64,4 +65,12 @@ public Location add(Number x, Number y, Number z) { this.z += z.doubleValue(); return this; } + + public static Location fromVector3D(Vector3D vector) { + return new Location(vector.getX(), vector.getY(), vector.getZ(), Math.toDegrees(vector.getAlpha()), Math.toDegrees(vector.getDelta())); + } + + public Location add(Location loc) { + return add(loc.getX(), loc.getY(), loc.getZ()); + } }