From 74a879212c94ea2fc7f2a36f7db043e67571cf08 Mon Sep 17 00:00:00 2001 From: Daniel Saukel Date: Fri, 19 Jun 2020 00:46:03 +0200 Subject: [PATCH] Add donors addon module --- addon/README.md | 7 + addon/core/pom.xml | 32 ++ .../de/erethon/dungeonsxxl/DungeonsXXL.java | 72 ++++ .../requirement/FeeItemsRequirement.java | 78 ++++ .../dungeonsxxl/sign/FireworkSign.java | 63 +++ .../dungeonsxxl/sign/GlowingBlockSign.java | 121 ++++++ .../dungeonsxxl/sign/InteractWallSign.java | 64 +++ .../dungeonsxxl/sign/ParticleSign.java | 88 ++++ .../dungeonsxxl/util/FireworkUtil.java | 66 +++ .../de/erethon/dungeonsxxl/util/GlowUtil.java | 380 ++++++++++++++++++ .../dungeonsxxl/world/block/GlowingBlock.java | 58 +++ addon/core/src/main/resources/plugin.yml | 7 + addon/dist/pom.xml | 37 ++ addon/pom.xml | 27 ++ .../erethon/dungeonsxl/config/DMessage.java | 1 + core/src/main/resources/languages/english.yml | 1 + core/src/main/resources/languages/french.yml | 1 + core/src/main/resources/languages/german.yml | 1 + pom.xml | 1 + 19 files changed, 1105 insertions(+) create mode 100644 addon/README.md create mode 100644 addon/core/pom.xml create mode 100644 addon/core/src/main/java/de/erethon/dungeonsxxl/DungeonsXXL.java create mode 100644 addon/core/src/main/java/de/erethon/dungeonsxxl/requirement/FeeItemsRequirement.java create mode 100644 addon/core/src/main/java/de/erethon/dungeonsxxl/sign/FireworkSign.java create mode 100644 addon/core/src/main/java/de/erethon/dungeonsxxl/sign/GlowingBlockSign.java create mode 100644 addon/core/src/main/java/de/erethon/dungeonsxxl/sign/InteractWallSign.java create mode 100644 addon/core/src/main/java/de/erethon/dungeonsxxl/sign/ParticleSign.java create mode 100644 addon/core/src/main/java/de/erethon/dungeonsxxl/util/FireworkUtil.java create mode 100644 addon/core/src/main/java/de/erethon/dungeonsxxl/util/GlowUtil.java create mode 100644 addon/core/src/main/java/de/erethon/dungeonsxxl/world/block/GlowingBlock.java create mode 100644 addon/core/src/main/resources/plugin.yml create mode 100644 addon/dist/pom.xml create mode 100644 addon/pom.xml diff --git a/addon/README.md b/addon/README.md new file mode 100644 index 00000000..b61a33d3 --- /dev/null +++ b/addon/README.md @@ -0,0 +1,7 @@ +## DungeonsXL Donors Addon + +(C) 2020 Daniel Saukel, All Rights Reserved. + +This module is a plugin with additional features made for donors. + +The GNU LGPLv3 of the API and the GNU GPLv3 license of the other modules do not apply to this module. diff --git a/addon/core/pom.xml b/addon/core/pom.xml new file mode 100644 index 00000000..d42464a8 --- /dev/null +++ b/addon/core/pom.xml @@ -0,0 +1,32 @@ + + 4.0.0 + de.erethon.dungeonsxl + dungeonsxl-addon-core + ${project.parent.version} + jar + + de.erethon.dungeonsxl + dungeonsxl-addon + 0.0.1-SNAPSHOT + + + + + . + true + src/main/resources/ + + plugin.yml + + + + + + + org.spigotmc + spigot + ${spigotVersion.latest} + provided + + + diff --git a/addon/core/src/main/java/de/erethon/dungeonsxxl/DungeonsXXL.java b/addon/core/src/main/java/de/erethon/dungeonsxxl/DungeonsXXL.java new file mode 100644 index 00000000..1e94e9cb --- /dev/null +++ b/addon/core/src/main/java/de/erethon/dungeonsxxl/DungeonsXXL.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2020 Daniel Saukel + * + * All rights reserved. + */ +package de.erethon.dungeonsxxl; + +import de.erethon.commons.compatibility.Internals; +import de.erethon.commons.javaplugin.DREPlugin; +import de.erethon.commons.javaplugin.DREPluginSettings; +import de.erethon.dungeonsxl.DungeonsXL; +import de.erethon.dungeonsxxl.requirement.*; +import de.erethon.dungeonsxxl.sign.*; +import de.erethon.dungeonsxxl.util.GlowUtil; + +/** + * @author Daniel Saukel + */ +public class DungeonsXXL extends DREPlugin { + + private DungeonsXL dxl; + private GlowUtil glowUtil; + + public DungeonsXXL() { + settings = DREPluginSettings.builder() + .internals(Internals.v1_15_R1) + .metrics(false) + .spigotMCResourceId(-1) + .build(); + } + + @Override + public void onEnable() { + dxl = DungeonsXL.getInstance(); + glowUtil = new GlowUtil(this); + + dxl.getRequirementRegistry().add("feeItems", FeeItemsRequirement.class); + + dxl.getSignRegistry().add("Firework", FireworkSign.class); + dxl.getSignRegistry().add("GlowingBlock", GlowingBlockSign.class); + dxl.getSignRegistry().add("InteractWall", InteractWallSign.class); + dxl.getSignRegistry().add("Particle", ParticleSign.class); + } + + /** + * Returns the instance of this plugin. + * + * @return the instance of this plugin + */ + public static DungeonsXXL getInstance() { + return (DungeonsXXL) DREPlugin.getInstance(); + } + + /** + * Returns the current {@link de.erethon.dungeonsxl.DungeonsXL} singleton. + * + * @return the current {@link de.erethon.dungeonsxl.DungeonsXL} singleton + */ + public DungeonsXL getDXL() { + return dxl; + } + + /** + * The loaded instance of GlowUtil. + * + * @return the loaded instance of GlowUtil + */ + public GlowUtil getGlowUtil() { + return glowUtil; + } + +} diff --git a/addon/core/src/main/java/de/erethon/dungeonsxxl/requirement/FeeItemsRequirement.java b/addon/core/src/main/java/de/erethon/dungeonsxxl/requirement/FeeItemsRequirement.java new file mode 100644 index 00000000..aabdc8f9 --- /dev/null +++ b/addon/core/src/main/java/de/erethon/dungeonsxxl/requirement/FeeItemsRequirement.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 Daniel Saukel + * + * All rights reserved. + */ +package de.erethon.dungeonsxxl.requirement; + +import de.erethon.dungeonsxl.api.DungeonsAPI; +import de.erethon.dungeonsxl.api.Requirement; +import de.erethon.dungeonsxl.config.DMessage; +import java.util.List; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +/** + * @author Daniel Saukel + */ +public class FeeItemsRequirement implements Requirement { + + private DungeonsAPI api; + + private List fee; + + public FeeItemsRequirement(DungeonsAPI api) { + this.api = api; + } + + public List getFee() { + return fee; + } + + @Override + public void setup(ConfigurationSection config) { + fee = api.getCaliburn().deserializeStackList(config, "feeItems"); + } + + @Override + public boolean check(Player player) { + for (ItemStack stack : fee) { + if (!player.getInventory().containsAtLeast(stack, stack.getAmount())) { + return false; + } + } + return true; + } + + @Override + public BaseComponent[] getCheckMessage(Player player) { + ComponentBuilder builder = new ComponentBuilder(DMessage.REQUIREMENT_FEE_ITEMS + ": ").color(ChatColor.GOLD); + boolean first = true; + for (ItemStack stack : fee) { + String name = stack.getAmount() > 1 ? stack.getAmount() + " " : "" + api.getCaliburn().getExItem(stack).getName(); + ChatColor color = player.getInventory().containsAtLeast(stack, stack.getAmount()) ? ChatColor.GREEN : ChatColor.DARK_RED; + if (!first) { + builder.append(", ").color(ChatColor.WHITE); + } else { + first = false; + } + builder.append(name).color(color); + } + return builder.create(); + } + + @Override + public void demand(Player player) { + player.getInventory().removeItem(fee.toArray(new ItemStack[]{})); + } + + @Override + public String toString() { + return "FeeItemsRequirement{items=" + fee + "}"; + } + +} diff --git a/addon/core/src/main/java/de/erethon/dungeonsxxl/sign/FireworkSign.java b/addon/core/src/main/java/de/erethon/dungeonsxxl/sign/FireworkSign.java new file mode 100644 index 00000000..ea5e6766 --- /dev/null +++ b/addon/core/src/main/java/de/erethon/dungeonsxxl/sign/FireworkSign.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2020 Daniel Saukel + * + * All rights reserved. + */ +package de.erethon.dungeonsxxl.sign; + +import de.erethon.dungeonsxl.api.DungeonsAPI; +import de.erethon.dungeonsxl.api.sign.Button; +import de.erethon.dungeonsxl.api.world.InstanceWorld; +import de.erethon.dungeonsxl.player.DPermission; +import de.erethon.dungeonsxxl.util.FireworkUtil; +import org.bukkit.block.Sign; + +/** + * @author Daniel Saukel + */ +public class FireworkSign extends Button { + + public FireworkSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { + super(api, sign, lines, instance); + } + + @Override + public String getName() { + return "Firework"; + } + + @Override + public String getBuildPermission() { + return DPermission.SIGN.getNode() + ".firework"; + } + + @Override + public boolean isOnDungeonInit() { + return false; + } + + @Override + public boolean isProtected() { + return false; + } + + @Override + public boolean isSetToAir() { + return true; + } + + @Override + public boolean validate() { + return true; + } + + @Override + public void initialize() { + } + + @Override + public void push() { + FireworkUtil.spawnRandom(getSign().getLocation()); + } + +} diff --git a/addon/core/src/main/java/de/erethon/dungeonsxxl/sign/GlowingBlockSign.java b/addon/core/src/main/java/de/erethon/dungeonsxxl/sign/GlowingBlockSign.java new file mode 100644 index 00000000..bf612545 --- /dev/null +++ b/addon/core/src/main/java/de/erethon/dungeonsxxl/sign/GlowingBlockSign.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2020 Daniel Saukel + * + * All rights reserved. + */ +package de.erethon.dungeonsxxl.sign; + +import de.erethon.commons.misc.BlockUtil; +import de.erethon.commons.misc.EnumUtil; +import de.erethon.dungeonsxl.api.DungeonsAPI; +import de.erethon.dungeonsxl.api.sign.Rocker; +import de.erethon.dungeonsxl.api.world.InstanceWorld; +import de.erethon.dungeonsxl.player.DPermission; +import de.erethon.dungeonsxl.world.DGameWorld; +import de.erethon.dungeonsxxl.DungeonsXXL; +import de.erethon.dungeonsxxl.world.block.GlowingBlock; +import org.bukkit.ChatColor; +import org.bukkit.block.Sign; + +/** + * Turns the attached block into a glowing block. + * + * @author Daniel Saukel + */ +public class GlowingBlockSign extends Rocker { + + private ChatColor color = ChatColor.DARK_RED; + private Double time; + + private GlowingBlock glowingBlock; + + public GlowingBlockSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { + super(api, sign, lines, instance); + } + + /** + * Returns the glowing block. + * + * @return the glowing block + */ + public GlowingBlock getGlowingBlock() { + return glowingBlock; + } + + /** + * Returns the color of the glowing block or null if it is a rainbow block. + * + * @return the color of the glowing block or null if it is a rainbow block + */ + public ChatColor getColor() { + return color; + } + + @Override + public String getName() { + return "GlowingBlock"; + } + + @Override + public String getBuildPermission() { + return DPermission.SIGN.getNode() + ".glowingblock"; + } + + @Override + public boolean isOnDungeonInit() { + return false; + } + + @Override + public boolean isProtected() { + return false; + } + + @Override + public boolean isSetToAir() { + return true; + } + + @Override + public boolean validate() { + return true; + } + + @Override + public void initialize() { + if (getLine(1).equalsIgnoreCase("RAINBOW")) { + color = null; + } else { + ChatColor color = EnumUtil.getEnumIgnoreCase(ChatColor.class, getLine(1)); + if (color != null) { + this.color = color; + } + } + try { + time = Double.parseDouble(getLine(2)); + } catch (NumberFormatException exception) { + } + } + + @Override + public void activate() { + if (active) { + return; + } + + ((DGameWorld) getGameWorld()).addGameBlock( + glowingBlock = new GlowingBlock(DungeonsXXL.getInstance(), BlockUtil.getAttachedBlock(getSign().getBlock()), color, time)); + active = true; + } + + @Override + public void deactivate() { + if (!active) { + return; + } + + glowingBlock.removeGlow(); + active = false; + } + +} diff --git a/addon/core/src/main/java/de/erethon/dungeonsxxl/sign/InteractWallSign.java b/addon/core/src/main/java/de/erethon/dungeonsxxl/sign/InteractWallSign.java new file mode 100644 index 00000000..12d952e9 --- /dev/null +++ b/addon/core/src/main/java/de/erethon/dungeonsxxl/sign/InteractWallSign.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2020 Daniel Saukel + * + * All rights reserved. + */ +package de.erethon.dungeonsxxl.sign; + +import de.erethon.commons.misc.BlockUtil; +import de.erethon.commons.misc.NumberUtil; +import de.erethon.dungeonsxl.api.DungeonsAPI; +import de.erethon.dungeonsxl.api.world.InstanceWorld; +import de.erethon.dungeonsxl.player.DPermission; +import de.erethon.dungeonsxl.sign.passive.InteractSign; +import de.erethon.dungeonsxl.trigger.InteractTrigger; +import de.erethon.dungeonsxl.world.DGameWorld; +import org.bukkit.block.Sign; + +/** + * This sign adds an interact trigger to an attached block, like a "suspicious wall". + * + * @author Daniel Saukel + */ +public class InteractWallSign extends InteractSign { + + public InteractWallSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { + super(api, sign, lines, instance); + } + + @Override + public String getName() { + return "InteractWall"; + } + + @Override + public String getBuildPermission() { + return DPermission.SIGN.getNode() + ".interactwall"; + } + + @Override + public boolean isOnDungeonInit() { + return false; + } + + @Override + public boolean isProtected() { + return true; + } + + @Override + public boolean isSetToAir() { + return true; + } + + @Override + public void initialize() { + InteractTrigger trigger = InteractTrigger.getOrCreate(NumberUtil.parseInt(getSign().getLine(1)), + BlockUtil.getAttachedBlock(getSign().getBlock()), (DGameWorld) getGameWorld()); + if (trigger != null) { + trigger.addListener(this); + addTrigger(trigger); + } + } + +} diff --git a/addon/core/src/main/java/de/erethon/dungeonsxxl/sign/ParticleSign.java b/addon/core/src/main/java/de/erethon/dungeonsxxl/sign/ParticleSign.java new file mode 100644 index 00000000..744bc536 --- /dev/null +++ b/addon/core/src/main/java/de/erethon/dungeonsxxl/sign/ParticleSign.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2020 Daniel Saukel + * + * All rights reserved. + */ +package de.erethon.dungeonsxxl.sign; + +import de.erethon.commons.misc.EnumUtil; +import de.erethon.commons.misc.NumberUtil; +import de.erethon.dungeonsxl.api.DungeonsAPI; +import de.erethon.dungeonsxl.api.sign.Button; +import de.erethon.dungeonsxl.api.world.InstanceWorld; +import de.erethon.dungeonsxl.player.DPermission; +import org.bukkit.Particle; +import org.bukkit.block.Sign; + +/** + * Spawns particles. + * + * @author Daniel Saukel + */ +public class ParticleSign extends Button { + + private Particle particle; + private int count; + private double offsetX, offsetY, offsetZ; + private double extra = 1; + + public ParticleSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { + super(api, sign, lines, instance); + } + + @Override + public String getName() { + return "Particle"; + } + + @Override + public String getBuildPermission() { + return DPermission.SIGN.getNode() + ".particle"; + } + + @Override + public boolean isOnDungeonInit() { + return false; + } + + @Override + public boolean isProtected() { + return false; + } + + @Override + public boolean isSetToAir() { + return true; + } + + @Override + public boolean validate() { + particle = EnumUtil.getEnumIgnoreCase(Particle.class, getLine(1)); + if (particle == null) { + markAsErroneous("Unknown particle type: " + getLine(1)); + return false; + } + return true; + } + + @Override + public void initialize() { + String[] args = getLine(2).split(","); + if (args.length == 1) { + extra = NumberUtil.parseDouble(args[0], 1); + } else if (args.length >= 3) { + offsetX = NumberUtil.parseDouble(args[0], 0); + offsetX = NumberUtil.parseDouble(args[1], 0); + offsetX = NumberUtil.parseDouble(args[2], 0); + if (args.length == 4) { + extra = NumberUtil.parseDouble(args[3], 1); + } + } + } + + @Override + public void push() { + getSign().getWorld().spawnParticle(particle, getSign().getLocation(), count, offsetX, offsetY, offsetZ, extra); + } + +} diff --git a/addon/core/src/main/java/de/erethon/dungeonsxxl/util/FireworkUtil.java b/addon/core/src/main/java/de/erethon/dungeonsxxl/util/FireworkUtil.java new file mode 100644 index 00000000..15438cb9 --- /dev/null +++ b/addon/core/src/main/java/de/erethon/dungeonsxxl/util/FireworkUtil.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 Daniel Saukel + * + * All rights reserved. + */ +package de.erethon.dungeonsxxl.util; + +import java.util.Random; +import org.bukkit.Color; +import static org.bukkit.Color.*; +import org.bukkit.FireworkEffect; +import org.bukkit.Location; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Firework; +import org.bukkit.inventory.meta.FireworkMeta; + +/** + * Util class for randomized fireworks. + * + * @author Daniel Saukel + */ +public class FireworkUtil { + + private static final Random RANDOM = new Random(); + private static final Color[] COLORS = {YELLOW, AQUA, BLACK, BLUE, FUCHSIA, GRAY, GREEN, LIME, MAROON, NAVY, OLIVE, ORANGE, PURPLE, RED, SILVER, TEAL, WHITE}; + + /** + * Spawns a randomized firework. + * + * @param location the location where the firework is fired + * @return the Firework + */ + public static Firework spawnRandom(Location location) { + Firework firework = (Firework) location.getWorld().spawnEntity(location, EntityType.FIREWORK); + FireworkMeta meta = firework.getFireworkMeta(); + Random r = new Random(); + int rt = r.nextInt(4) + 1; + FireworkEffect.Type type = FireworkEffect.Type.BALL; + if (rt == 1) { + type = FireworkEffect.Type.BALL; + } + if (rt == 2) { + type = FireworkEffect.Type.BALL_LARGE; + } + if (rt == 3) { + type = FireworkEffect.Type.BURST; + } + if (rt == 4) { + type = FireworkEffect.Type.CREEPER; + } + if (rt == 5) { + type = FireworkEffect.Type.STAR; + } + FireworkEffect effect = FireworkEffect.builder().flicker(r.nextBoolean()).withColor(randomColor()).withFade(randomColor()).with(type).trail(r.nextBoolean()).build(); + meta.addEffect(effect); + int rp = r.nextInt(2) + 1; + meta.setPower(rp); + firework.setFireworkMeta(meta); + return firework; + } + + private static Color randomColor() { + return COLORS[RANDOM.nextInt(COLORS.length - 1)]; + } + +} diff --git a/addon/core/src/main/java/de/erethon/dungeonsxxl/util/GlowUtil.java b/addon/core/src/main/java/de/erethon/dungeonsxxl/util/GlowUtil.java new file mode 100644 index 00000000..efd62a2b --- /dev/null +++ b/addon/core/src/main/java/de/erethon/dungeonsxxl/util/GlowUtil.java @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2020 Daniel Saukel + * + * All rights reserved. + */ +package de.erethon.dungeonsxxl.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Random; +import net.minecraft.server.v1_15_R1.EntityShulker; +import net.minecraft.server.v1_15_R1.EntityTypes; +import net.minecraft.server.v1_15_R1.Packet; +import net.minecraft.server.v1_15_R1.PacketPlayOutEntityDestroy; +import net.minecraft.server.v1_15_R1.PacketPlayOutSpawnEntityLiving; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.entity.Shulker; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.plugin.Plugin; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scoreboard.Team; + +/** + * @author Daniel Saukel + */ +public class GlowUtil implements Listener { + + private static final Random RANDOM = new Random(); + + private Map teams = new HashMap<>(); + private GlowData glowingBlocks = new GlowData<>(); + private Map> playerGlows = new HashMap<>(); + private GlowRunnable runnable = new GlowRunnable(); + + public GlowUtil(Plugin plugin) { + runnable.runTaskTimer(plugin, 0L, 2L); + Bukkit.getPluginManager().registerEvents(this, plugin); + } + + private Team getTeam(ChatColor color) { + if (!teams.containsKey(color)) { + Team team = Bukkit.getScoreboardManager().getMainScoreboard().getTeam("DXL_" + color.getChar()); + if (team == null) { + team = Bukkit.getScoreboardManager().getMainScoreboard().registerNewTeam("DXL_" + color.getChar()); + team.setColor(color); + } + teams.put(color, team); + } + return teams.get(color); + } + + /** + * Adds a colored glow effect to the block that is visible to all players. + * + * @param block the block + * @param color the glow color + * @return the spawned entity that provides the glow effect + */ + public org.bukkit.entity.Entity addBlockGlow(Block block, ChatColor color) { + Shulker entity = block.getWorld().spawn(new Location(block.getWorld(), block.getX() + .5, block.getY(), block.getZ() + .5), Shulker.class); + entity.setAI(false); + entity.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 0), true); + entity.setInvulnerable(true); + addGlow(entity, color); + glowingBlocks.put(block, entity); + return entity; + } + + /** + * Adds a packet-level colored glow effect to the block that is only visible to certain players. + * + * @param block the block + * @param color the glow color + * @param players the players who can see the effect + */ + public void addBlockGlow(Block block, ChatColor color, Player... players) { + EntityShulker entity = new EntityShulker(EntityTypes.SHULKER, ((CraftWorld) block.getWorld()).getHandle()); + entity.setLocation(block.getX() + .5, block.getY(), block.getZ() + .5, 0, 0); + entity.setFlag(6, true); + entity.setInvisible(true); + for (Player player : players) { + sendPacket(player, new PacketPlayOutSpawnEntityLiving(entity)); + if (playerGlows.get(player) == null) { + playerGlows.put(player, new GlowData<>()); + } + playerGlows.get(player).put(block, entity); + } + } + + /** + * Adds a rainbow colored glow effect to the block that is visible to all players. + * + * @param block the block + * @return the spawned entity that provides the glow effect + */ + public org.bukkit.entity.Entity addRainbowBlockGlow(Block block) { + return addRainbowBlockGlow(block, (Long) null); + } + + /** + * Adds a rainbow colored glow effect to the block that is visible to all players. + *

+ * The task is cancelled automatically when the entity dies. + * + * @param block the block + * @param cancelTime the time in milliseconds until the glow effect shall end; null = forever + * @return the spawned entity that provides the glow effect + */ + public org.bukkit.entity.Entity addRainbowBlockGlow(Block block, Long cancelTime) { + Shulker entity = block.getWorld().spawn(new Location(block.getWorld(), block.getX() + .5, block.getY(), block.getZ() + .5), Shulker.class); + entity.setAI(false); + entity.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 0), true); + entity.setInvulnerable(true); + glowingBlocks.put(block, entity); + addRainbowGlow(entity, cancelTime); + return entity; + } + + /** + * Adds a packet-level rainbow colored glow effect to the block that is only visible to certain players. + *

+ * Returns the repeating task that handles color changes. + * + * @param block the block + * @param players + */ + public void addRainbowBlockGlow(Block block, Player... players) { + addRainbowBlockGlow(block, null, players); + } + + /** + * Adds a packet-level rainbow colored glow effect to the block that is only visible to certain players. + *

+ * Returns the repeating task that handles color changes. + * + * @param block the block + * @param cancelTime the time in milliseconds until the glow effect shall end; null = forever + * @param players + */ + public void addRainbowBlockGlow(Block block, Long cancelTime, Player... players) { + EntityShulker entity = new EntityShulker(EntityTypes.SHULKER, ((CraftWorld) block.getWorld()).getHandle()); + entity.setLocation(block.getX() + .5, block.getY(), block.getZ() + .5, 0, 0); + entity.setFlag(6, true); + entity.setInvisible(true); + for (Player player : players) { + sendPacket(player, new PacketPlayOutSpawnEntityLiving(entity)); + if (playerGlows.get(player) == null) { + playerGlows.put(player, new GlowData<>()); + } + playerGlows.get(player).put(block, entity); + } + addRainbowGlow(entity, cancelTime); + } + + /** + * Removes the glow effect from a glowing block. + * + * @param block the block + */ + public void removeBlockGlow(Block block) { + org.bukkit.entity.Entity bukkitEntity = glowingBlocks.get(block); + if (bukkitEntity != null) { + bukkitEntity.remove(); + glowingBlocks.remove(block); + runnable.removeEntity(bukkitEntity); + } + + for (Entry> entry : playerGlows.entrySet()) { + net.minecraft.server.v1_15_R1.Entity nmsEntity = entry.getValue().get(block); + if (nmsEntity != null) { + sendPacket(entry.getKey(), new PacketPlayOutEntityDestroy(nmsEntity.getId())); + runnable.removeEntity(nmsEntity); + } + } + } + + /** + * Adds a colored glow effect to an entity and handles its scoreboard team membership. + * + * @param entity a Bukkit Entity + * @param color the glow color + */ + public void addGlow(org.bukkit.entity.Entity entity, ChatColor color) { + getTeam(color).addEntry(asEntry(entity)); + entity.setGlowing(true); + } + + /** + * Adds a colored glow effect to an entity and handles its scoreboard team membership. + * + * @param entity an NMS Entity + * @param color the glow color + */ + public void addGlow(net.minecraft.server.v1_15_R1.Entity entity, ChatColor color) { + getTeam(color).addEntry(asEntry(entity)); + entity.setFlag(6, true); + } + + /** + * Adds a changing glow effect to an entity. + * + * @param entity an NMS Entity + */ + public void addRainbowGlow(org.bukkit.entity.Entity entity) { + addRainbowGlow(entity, null); + } + + /** + * Adds a changing glow effect to an entity. + * + * @param entity an NMS Entity + * @param cancelTime the time in milliseconds until the glow effect shall end; null = forever + */ + public void addRainbowGlow(org.bukkit.entity.Entity entity, Long cancelTime) { + entity.setGlowing(true); + runnable.addEntity(entity, cancelTime != null ? System.currentTimeMillis() + cancelTime : null); + } + + /** + * Adds a changing glow effect to an entity. + * + * @param entity an NMS Entity + */ + public void addRainbowGlow(net.minecraft.server.v1_15_R1.Entity entity) { + addRainbowGlow(entity, null); + } + + /** + * Adds a changing glow effect to an entity. + * + * @param entity an NMS Entity + * @param cancelTime the time in milliseconds until the glow effect shall end; null = forever + */ + public void addRainbowGlow(net.minecraft.server.v1_15_R1.Entity entity, Long cancelTime) { + entity.setFlag(6, true); + runnable.addEntity(entity, cancelTime != null ? System.currentTimeMillis() + cancelTime : null); + } + + /** + * Removes the glow effect from an entity and handles its scoreboard team membership. + * + * @param entity a Bukkit Entity + */ + public void removeGlow(org.bukkit.entity.Entity entity) { + entity.setGlowing(false); + teams.values().forEach(t -> t.removeEntry(asEntry(entity))); + runnable.removeEntity(entity); + } + + /** + * Removes the glow effect from an entity and handles its scoreboard team membership. + * + * @param entity an NMS Entity + */ + public void removeGlow(net.minecraft.server.v1_15_R1.Entity entity) { + entity.setFlag(6, false); + teams.values().forEach(t -> t.removeEntry(asEntry(entity))); + runnable.removeEntity(entity); + } + + private static String asEntry(org.bukkit.entity.Entity entity) { + return entity instanceof Player ? entity.getName() : entity.getUniqueId().toString(); + } + + private static String asEntry(net.minecraft.server.v1_15_R1.Entity entity) { + return entity instanceof Player ? entity.getName() : entity.getUniqueID().toString(); + } + + private static void sendPacket(Player player, Packet packet) { + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); + } + + @EventHandler + public void onBlockBreak(BlockBreakEvent event) { + removeBlockGlow(event.getBlock()); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + playerGlows.remove(event.getPlayer()); + } + + private class GlowRunnable extends BukkitRunnable { + + private Map entities = new HashMap<>(); + private ChatColor color; + + private void addEntity(Object entity, Long cancelTime) { + entities.put(entity, cancelTime); + } + + private void removeEntity(Object entity) { + entities.remove(entity); + } + + @Override + public void run() { + color = ChatColor.values()[RANDOM.nextInt(ChatColor.values().length - 1)]; + for (Entry entry : entities.entrySet().toArray(new Entry[entities.size()])) { + if (entry.getKey() instanceof org.bukkit.entity.Entity) { + run((org.bukkit.entity.Entity) entry.getKey(), entry.getValue()); + } else if (entry.getKey() instanceof net.minecraft.server.v1_15_R1.Entity) { + run((net.minecraft.server.v1_15_R1.Entity) entry.getKey(), entry.getValue()); + } + } + } + + private void run(org.bukkit.entity.Entity entity, Long cancelTime) { + getTeam(color).removeEntry(asEntry(entity)); + if ((cancelTime != null && System.currentTimeMillis() >= cancelTime) || entity.isDead()) { + entities.remove(entity); + glowingBlocks.remove(entity); + if (!entity.isDead()) { + entity.setGlowing(false); + } else { + entity.remove(); + } + return; + } + getTeam(color).addEntry(asEntry(entity)); + } + + private void run(net.minecraft.server.v1_15_R1.Entity entity, Long cancelTime) { + getTeam(color).removeEntry(asEntry(entity)); + if (cancelTime != null && System.currentTimeMillis() >= cancelTime) { + entities.remove(entity); + for (Entry> entry : playerGlows.entrySet()) { + if (!entry.getValue().glowingBlocks.containsValue(entity)) { + continue; + } + Player player = entry.getKey(); + sendPacket(player, new PacketPlayOutEntityDestroy(entity.getId())); + entry.getValue().remove(entity); + } + return; + } + getTeam(color).addEntry(asEntry(entity)); + } + + } + + static class GlowData { + + Map glowingBlocks = new HashMap<>(); + + T get(Block block) { + return glowingBlocks.get(block); + } + + void remove(Block block) { + glowingBlocks.remove(block); + } + + void remove(T entity) { + for (Entry entry : glowingBlocks.entrySet().toArray(new Entry[glowingBlocks.size()])) { + if (entry.getValue().equals(entity)) { + glowingBlocks.remove(entry.getKey()); + } + } + } + + void put(Block block, T entity) { + glowingBlocks.put(block, entity); + } + + } + +} diff --git a/addon/core/src/main/java/de/erethon/dungeonsxxl/world/block/GlowingBlock.java b/addon/core/src/main/java/de/erethon/dungeonsxxl/world/block/GlowingBlock.java new file mode 100644 index 00000000..611b04c7 --- /dev/null +++ b/addon/core/src/main/java/de/erethon/dungeonsxxl/world/block/GlowingBlock.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2020 Daniel Saukel + * + * All rights reserved. + */ +package de.erethon.dungeonsxxl.world.block; + +import de.erethon.dungeonsxl.world.block.GameBlock; +import de.erethon.dungeonsxxl.DungeonsXXL; +import de.erethon.dungeonsxxl.util.GlowUtil; +import org.bukkit.ChatColor; +import org.bukkit.block.Block; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.scheduler.BukkitRunnable; + +/** + * @author Daniel Saukel + */ +public class GlowingBlock extends GameBlock { + + private GlowUtil glowUtil; + + public GlowingBlock(DungeonsXXL plugin, Block block, ChatColor color, Double time) { + super(plugin.getDXL(), block); + glowUtil = plugin.getGlowUtil(); + + Long millis; + if (time != null) { + millis = (long) (time * 1000); + } else { + millis = null; + } + + if (color != null) { + glowUtil.addBlockGlow(block, color); + if (millis != null) { + new BukkitRunnable() { + @Override + public void run() { + removeGlow(); + } + }.runTaskLater(plugin, millis / 50); + } + } else { + glowUtil.addRainbowBlockGlow(block, millis); + } + } + + public void removeGlow() { + glowUtil.removeBlockGlow(block); + } + + @Override + public boolean onBreak(BlockBreakEvent event) { + return false; + } + +} diff --git a/addon/core/src/main/resources/plugin.yml b/addon/core/src/main/resources/plugin.yml new file mode 100644 index 00000000..8b02521f --- /dev/null +++ b/addon/core/src/main/resources/plugin.yml @@ -0,0 +1,7 @@ +name: DungeonsXXL +main: de.erethon.dungeonsxxl.DungeonsXXL +version: ${project.version}${buildNo} +author: Daniel Saukel +description: ${project.description} +website: ${project.url} +depend: [DungeonsXL] diff --git a/addon/dist/pom.xml b/addon/dist/pom.xml new file mode 100644 index 00000000..eff2b917 --- /dev/null +++ b/addon/dist/pom.xml @@ -0,0 +1,37 @@ + + 4.0.0 + de.erethon.dungeonsxl + dungeonsxl-addon-dist + ${project.parent.version} + jar + + de.erethon.dungeonsxl + dungeonsxl-addon + 0.0.1-SNAPSHOT + + + ${project.artifactId}-${project.version}${buildNo} + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + + + + + + + de.erethon.dungeonsxl + dungeonsxl-addon-core + ${project.parent.version} + + + diff --git a/addon/pom.xml b/addon/pom.xml new file mode 100644 index 00000000..ea819ac6 --- /dev/null +++ b/addon/pom.xml @@ -0,0 +1,27 @@ + + 4.0.0 + de.erethon.dungeonsxl + dungeonsxl-addon + 0.0.1-SNAPSHOT + pom + DungeonsXXL + https://dre2n.github.io + Create BETTER custom dungeons and adventure maps with ease! + + de.erethon.dungeonsxl + dungeonsxl-parent + 0.18-SNAPSHOT + + + core + dist + + + + de.erethon.dungeonsxl + dungeonsxl-dist + 0.18-SNAPSHOT + provided + + + diff --git a/core/src/main/java/de/erethon/dungeonsxl/config/DMessage.java b/core/src/main/java/de/erethon/dungeonsxl/config/DMessage.java index fb4ae828..3ae5b03c 100644 --- a/core/src/main/java/de/erethon/dungeonsxl/config/DMessage.java +++ b/core/src/main/java/de/erethon/dungeonsxl/config/DMessage.java @@ -208,6 +208,7 @@ public enum DMessage implements Message { PLAYER_UNLIMITED_LIVES("player.unlimitedLives"), PLAYER_WAIT_FOR_OTHER_PLAYERS("player.waitForOtherPlayers"), REQUIREMENT_FEE("requirement.fee"), + REQUIREMENT_FEE_ITEMS("requirement.feeItems"), REQUIREMENT_FEE_LEVEL("requirement.feeLevel"), REQUIREMENT_FEE_MONEY("requirement.feeMoney"), REQUIREMENT_FORBIDDEN_ITEMS("requirement.forbiddenItems"), diff --git a/core/src/main/resources/languages/english.yml b/core/src/main/resources/languages/english.yml index 2140311e..bc1ab8b8 100644 --- a/core/src/main/resources/languages/english.yml +++ b/core/src/main/resources/languages/english.yml @@ -224,6 +224,7 @@ player: waitForOtherPlayers: "&6Waiting for team members..." requirement: fee: "&6You have been charged &4&v1 &6for entering the dungeon." + feeItems: "Items" feeLevel: "Levels" feeMoney: "Money" forbiddenItems: "Forbidden items" diff --git a/core/src/main/resources/languages/french.yml b/core/src/main/resources/languages/french.yml index ad329f93..3a2c50f8 100644 --- a/core/src/main/resources/languages/french.yml +++ b/core/src/main/resources/languages/french.yml @@ -221,6 +221,7 @@ player: waitForOtherPlayers: "&6En attente des membres de l'équipe..." requirement: fee: "&6Vous avez été débité de &4&v1 &6pour entrer dans le donjon." + feeItems: "Items" feeLevel: "Niveaux" feeMoney: "Argent" forbiddenItems: "Objets défendus" diff --git a/core/src/main/resources/languages/german.yml b/core/src/main/resources/languages/german.yml index ccd988f5..41210aa6 100644 --- a/core/src/main/resources/languages/german.yml +++ b/core/src/main/resources/languages/german.yml @@ -221,6 +221,7 @@ player: waitForOtherPlayers: "&6Warte auf andere Gruppenmitglieder..." requirement: fee: "&6Du hast &4&v1 &6bezahlt, um den Dungeon zu betreten." + feeItems: "Items" feeLevel: "Level" feeMoney: "Geld" forbiddenItems: "Verbotene Items" diff --git a/pom.xml b/pom.xml index 553356fa..bfec5eca 100644 --- a/pom.xml +++ b/pom.xml @@ -14,6 +14,7 @@ bukkit_magicvalues core dist + addon