diff --git a/Battle-Crates-main/.gitignore b/Battle-Crates-main/.gitignore
new file mode 100644
index 0000000..4a410a1
--- /dev/null
+++ b/Battle-Crates-main/.gitignore
@@ -0,0 +1,70 @@
+# Eclipse
+/.classpath
+/.project
+/.settings
+
+# Netbeans
+/nbproject
+
+# Fuck mauri!
+/build.xml
+
+# Maven
+/target
+/dependency-reduced-pom.xml
+*/target
+*/dependency-reduced-pom.xml
+
+# Vim
+.*.sw[a-p]
+
+# Various other potential build files
+/build
+/bin
+/dist
+/manifest.mf
+/MANIFEST.MF
+/META-INF/MANIFEST.MF
+git.properties
+.ssh
+key
+commiter.sh
+key.pub
+dependency-reduced-pom.xml
+
+# Mac Filesystem Dust
+.DS_Store
+
+# IntelliJ IDEA
+*.iml
+*.ipr
+*.iws
+.idea/
+
+#Libraries jar files
+libraries/*.jar
+=======
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+out
\ No newline at end of file
diff --git a/Battle-Crates-main/pom.xml b/Battle-Crates-main/pom.xml
new file mode 100644
index 0000000..f9eda53
--- /dev/null
+++ b/Battle-Crates-main/pom.xml
@@ -0,0 +1,37 @@
+
+
+ 4.0.0
+
+ rip.battle.crates
+ BattleCrates
+ 1.0-SNAPSHOT
+
+
+ 8
+ 8
+
+
+
+
+ rip.battle.spigot
+ battlespigot-server
+ 1.7.10-R0.1-SNAPSHOT
+ provided
+
+
+ rip.battle.core
+ bukkit
+ 1.0-SNAPSHOT
+ provided
+
+
+ cc.stormworth.hcf
+ hcf
+ 1.0.0-SNAPSHOT
+ provided
+
+
+
+
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/Crates.java b/Battle-Crates-main/src/main/java/rip/battle/crates/Crates.java
new file mode 100644
index 0000000..a5ec72a
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/Crates.java
@@ -0,0 +1,69 @@
+package rip.battle.crates;
+
+import cc.stormworth.core.util.command.rCommandHandler;
+import com.mongodb.MongoClient;
+import com.mongodb.MongoClientOptions;
+import com.mongodb.MongoCredential;
+import com.mongodb.ServerAddress;
+import com.mongodb.client.MongoDatabase;
+import lombok.Getter;
+import org.bukkit.Bukkit;
+import org.bukkit.configuration.Configuration;
+import org.bukkit.plugin.java.JavaPlugin;
+import rip.battle.crates.airdrop.AirdropListener;
+import rip.battle.crates.crate.Crate;
+import rip.battle.crates.crate.listeners.CrateListener;
+import rip.battle.crates.reward.RewardsListeners;
+
+@Getter
+public class Crates extends JavaPlugin {
+
+ @Getter private static Crates instance;
+
+ private MongoDatabase mongoDatabase;
+
+ @Override
+ public void onEnable() {
+ instance = this;
+
+ saveDefaultConfig();
+ loadMongo();
+
+ Crate.load();
+
+ Bukkit.getPluginManager().registerEvents(new CrateListener(), this);
+ Bukkit.getPluginManager().registerEvents(new RewardsListeners(), this);
+ Bukkit.getPluginManager().registerEvents(new AirdropListener(), this);
+
+ rCommandHandler.registerPackage(this, "rip.battle.crates.crate.commands");
+ rCommandHandler.registerPackage(this, "rip.battle.crates.reward.commands");
+ }
+
+ @Override
+ public void onDisable() {
+ Crate.getCrates().values().forEach(Crate::save);
+ }
+
+ private void loadMongo() {
+ Configuration configuration = getConfig();
+ if (configuration.getBoolean("mongo.authentication.enabled")) {
+ ServerAddress serverAddress =
+ new ServerAddress(
+ configuration.getString("mongo.host"), configuration.getInt("mongo.port"));
+
+ MongoCredential credential =
+ MongoCredential.createCredential(
+ configuration.getString("mongo.authentication.username"),
+ "admin",
+ configuration.getString("mongo.authentication.password").toCharArray());
+
+ mongoDatabase =
+ new MongoClient(serverAddress, credential, MongoClientOptions.builder().build())
+ .getDatabase(getConfig().getString("mongo.database"));
+ } else {
+ mongoDatabase =
+ new MongoClient(configuration.getString("mongo.host"), configuration.getInt("mongo.port"))
+ .getDatabase(getConfig().getString("mongo.database"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/airdrop/Airdrop.java b/Battle-Crates-main/src/main/java/rip/battle/crates/airdrop/Airdrop.java
new file mode 100644
index 0000000..411cb37
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/airdrop/Airdrop.java
@@ -0,0 +1,82 @@
+package rip.battle.crates.airdrop;
+
+import cc.stormworth.core.kt.util.ItemBuilder;
+import cc.stormworth.core.util.trail.ParticleEffect;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.metadata.FixedMetadataValue;
+import rip.battle.crates.Crates;
+import rip.battle.crates.crate.Crate;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class Airdrop extends Crate {
+
+ public Airdrop() {
+ super("Airdrop");
+ getCrates().put("Airdrop", this);
+ }
+
+ @Override
+ public List getHologramsLines() {
+ return Arrays.asList(
+ getDisplayName() + " Airdrop",
+ "&7",
+ "&fLeft click &7for preview rewards",
+ "&7",
+ "&7&ostore.battle.rip");
+ }
+
+ @Override
+ public ItemStack generateKey() {
+ return new ItemBuilder(Material.CHEST)
+ .name("&6&lAirdrop")
+ .addToLore(
+ "&7Purchasable at &fstore.battle.rip&7.",
+ "",
+ "&ePlace this down to designate a &6&lLocation &efor the &6&lLoot&e!"
+ ).build();
+ }
+
+ public void startAnimation(Block block, Player player) {
+ createFlameRings(block.getLocation().add(0, 0, 0.5));
+
+ block.setMetadata("airdrop", new FixedMetadataValue(Crates.getInstance(), true));
+
+ /*Dropper dropper = (Dropper) block.getState();
+
+ List rewardList = new ArrayList<>(getRewards());
+ Collections.shuffle(rewardList);
+
+ int i = 0;
+
+ while (i <= 9) {
+ Reward randomReward = RandomUtils.getRandomReward(rewardList);
+
+ dropper.getInventory().setItem(i, randomReward.getItem());
+
+ if (!randomReward.getBroadcast().isEmpty()) {
+ randomReward.getBroadcast().forEach(line -> Bukkit.broadcastMessage(line.replace("{player}", player.getName())));
+ }
+
+ i++;
+ }*/
+ }
+
+ private void createFlameRings(Location location) {
+ double alpha = 0;
+ for (int count = 0; count < 50; count++) {
+ alpha += Math.PI / 16;
+
+ Location firstLocation = location.clone().add(Math.cos(alpha), Math.sin(alpha) + 1, Math.sin(alpha));
+ Location secondLocation = location.clone().add(Math.cos(alpha + Math.PI), Math.sin(alpha) + 1, Math.sin(alpha + Math.PI));
+
+ ParticleEffect.FLAME.display(0, 0, 0, 0, 10, firstLocation, 10);
+ ParticleEffect.FLAME.display(0, 0, 0, 0, 10, secondLocation, 10);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/airdrop/AirdropListener.java b/Battle-Crates-main/src/main/java/rip/battle/crates/airdrop/AirdropListener.java
new file mode 100644
index 0000000..a8a2dd7
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/airdrop/AirdropListener.java
@@ -0,0 +1,246 @@
+package rip.battle.crates.airdrop;
+
+import cc.stormworth.core.util.chat.CC;
+import cc.stormworth.core.util.general.LocationUtil;
+import cc.stormworth.core.util.general.TaskUtil;
+import cc.stormworth.hcf.Main;
+import cc.stormworth.hcf.team.Team;
+import cc.stormworth.hcf.team.claims.LandBoard;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.Sound;
+import org.bukkit.block.Block;
+import org.bukkit.block.Skull;
+import org.bukkit.craftbukkit.v1_7_R4.entity.CraftPlayer;
+import org.bukkit.entity.FallingBlock;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockPlaceEvent;
+import org.bukkit.event.entity.EntityChangeBlockEvent;
+import org.bukkit.event.inventory.InventoryCloseEvent;
+import org.bukkit.event.player.PlayerInteractEvent;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.metadata.FixedMetadataValue;
+import org.bukkit.scheduler.BukkitRunnable;
+import rip.battle.crates.Crates;
+import rip.battle.crates.crate.Crate;
+import rip.battle.crates.reward.Reward;
+import rip.battle.crates.utils.FireworkUtil;
+import rip.battle.crates.utils.ItemUtils;
+import rip.battle.crates.utils.RandomUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class AirdropListener implements Listener {
+
+ @EventHandler
+ public void onBlockPlaceEvent(BlockPlaceEvent event) {
+ Player player = event.getPlayer();
+
+ ItemStack item = event.getItemInHand();
+
+ if (!ItemUtils.isAirDrop(item)) {
+ return;
+ }
+
+ Airdrop airdrop = (Airdrop) Crate.getByName("Airdrop");
+ if (!airdrop.isEnable()) {
+ player.sendMessage(CC.translate("&cAirdrop is currently disabled."));
+ event.setCancelled(true);
+ player.playSound(player.getLocation(), Sound.ANVIL_BREAK, 1, 1);
+ return;
+ }
+
+ if (!player.isOp()) {
+
+ Team team = LandBoard.getInstance().getTeam(player.getLocation());
+
+ if (team != null && !team.isMember(player.getUniqueId())) {
+ player.sendMessage(CC.translate("&cYou can only put the &6&lAirdrop&c in your claim or in Wilderness."));
+ event.setCancelled(true);
+ return;
+ }
+
+ if (Main.getInstance().getServerHandler().isWarzone(player.getLocation())) {
+ player.sendMessage(CC.translate("&cYou cannot place an &6&lAirdrop&c in a Warzone."));
+ event.setCancelled(true);
+ return;
+ }
+ }
+
+ Location location = event.getBlock().getLocation().clone().add(0, 25, 0);
+
+ if (location.getBlock() != null && location.getBlock().getType() != Material.AIR) {
+ player.sendMessage(CC.translate("&cYou cannot place an &6&lAirdrop&c there."));
+ player.sendMessage(CC.translate("&cGo out in the open air to deploy the &6&lAirdrop&c."));
+ player.playSound(player.getLocation(), Sound.VILLAGER_NO, 1, 1);
+ event.setCancelled(true);
+ return;
+ }
+
+ FallingBlock block = player.getLocation().getWorld().spawnFallingBlock(
+ location,
+ Material.SNOW_BLOCK,
+ (byte) 3);
+
+ block.setMetadata("airdrop", new FixedMetadataValue(Crates.getInstance(), player.getName()));
+
+ event.setCancelled(true);
+
+ FireworkUtil.launchFirework(event.getBlock().getLocation());
+
+ ItemUtils.consume(player);
+ }
+
+ @EventHandler
+ public void onDeath(EntityChangeBlockEvent event) {
+ if (!(event.getEntity() instanceof FallingBlock)) {
+ return;
+ }
+
+ FallingBlock fallingBlock = (FallingBlock) event.getEntity();
+
+ if (!fallingBlock.hasMetadata("airdrop")) {
+ return;
+ }
+
+ Airdrop airdrop = (Airdrop) Crate.getByName("Airdrop");
+
+ Player player = Bukkit.getPlayer(fallingBlock.getMetadata("airdrop").get(0).asString());
+
+ fallingBlock.setDropItem(false);
+
+ Block block = event.getBlock();
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ block.setType(Material.SKULL);
+ block.setData((byte) 3);
+ Skull skull = (Skull) block.getState();
+ skull.setOwner("conhost");
+ skull.update();
+ }
+ }.runTaskLater(Crates.getInstance(), 2L);
+ for(Player online : Bukkit.getServer().getOnlinePlayers()) {
+ if(((CraftPlayer) online).getHandle().playerConnection.networkManager.getVersion() < 47){
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ online.sendBlockChange(block.getLocation(), Material.CHEST, (byte) 0);
+ }
+ }.runTaskLater(Crates.getInstance(), 2L);
+ }
+ }
+
+ block.setMetadata("airdrop", new FixedMetadataValue(Crates.getInstance(), player.getName()));
+
+ TaskUtil.runAsync(Crates.getInstance(), () -> airdrop.startAnimation(block, player));
+ }
+
+ /*@EventHandler
+ public void onEntityChangeBlock(EntityChangeBlockEvent event) {
+
+ if (!(event.getEntity() instanceof FallingBlock)) {
+ return;
+ }
+
+ FallingBlock fallingBlock = (FallingBlock) event.getEntity();
+
+ if (!fallingBlock.hasMetadata("airdrop")) {
+ return;
+ }
+
+ Airdrop airdrop = (Airdrop) Crate.getByName("Airdrop");
+
+ Player player = Bukkit.getPlayer(fallingBlock.getMetadata("airdrop").get(0).asString());
+
+ Block block = event.getBlock();
+
+ block.setType(Material.DROPPER);
+
+ airdrop.startAnimation(block, player);
+ }*/
+
+ @EventHandler
+ public void onInteract(PlayerInteractEvent event) {
+ Player player = event.getPlayer();
+
+ if (event.getClickedBlock() == null) {
+ return;
+ }
+
+ Block block = event.getClickedBlock();
+
+ if (!block.hasMetadata("airdrop")) {
+ return;
+ }
+ event.setCancelled(true);
+ if(block.hasMetadata("airdropOpen")) return;
+
+ if (block.getType() != Material.DROPPER) {
+ Inventory inventory = Bukkit.createInventory(null, 9, "Airdrop");
+
+ List rewardList = new ArrayList<>(Airdrop.getByName("Airdrop").getRewards());
+ Collections.shuffle(rewardList);
+
+ int i = 0;
+
+ while (i < 9) {
+ Reward randomReward = RandomUtils.getRandomReward(rewardList);
+
+ inventory.setItem(i, randomReward.getItem());
+
+ if (!randomReward.getBroadcast().isEmpty()) {
+ randomReward.getBroadcast().forEach(line -> Bukkit.broadcastMessage(line.replace("{player}", player.getName())));
+ }
+
+ i++;
+ }
+
+ player.openInventory(inventory);
+ }
+ block.setMetadata("airdropOpen", new FixedMetadataValue(Crates.getInstance(), true));
+
+ player.setMetadata("airdropOpen", new FixedMetadataValue(Crates.getInstance(), LocationUtil.parseLocation(block.getLocation())));
+
+ }
+
+ @EventHandler
+ public void onInventoryClose(InventoryCloseEvent event) {
+ Player player = (Player) event.getPlayer();
+
+ if (!player.hasMetadata("airdropOpen")) {
+ return;
+ }
+
+ if (!event.getInventory().getName().equals("Airdrop")) {
+ return;
+ }
+
+ Inventory inventory = event.getInventory();
+
+ for (int i = 0; i < inventory.getSize(); i++) {
+ ItemStack item = inventory.getItem(i);
+
+ if (item == null) {
+ continue;
+ }
+
+ player.getLocation().getWorld().dropItem(player.getLocation(), item);
+ }
+
+ Location location = LocationUtil.convertLocation(player.getMetadata("airdropOpen").get(0).asString());
+
+ Block block = location.getBlock();
+
+ block.setType(Material.AIR);
+
+ player.removeMetadata("airdropOpen", Crates.getInstance());
+ block.removeMetadata("airdrop", Crates.getInstance());
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/crate/Crate.java b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/Crate.java
new file mode 100644
index 0000000..ede9e43
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/Crate.java
@@ -0,0 +1,419 @@
+package rip.battle.crates.crate;
+
+import cc.stormworth.core.CorePlugin;
+import cc.stormworth.core.kt.util.ItemBuilder;
+import cc.stormworth.core.util.chat.CC;
+import cc.stormworth.core.util.general.LocationUtil;
+import cc.stormworth.core.util.holograms.Hologram;
+import cc.stormworth.core.util.holograms.Holograms;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.model.Filters;
+import com.mongodb.client.model.ReplaceOptions;
+import lombok.Getter;
+import lombok.Setter;
+import org.bson.Document;
+import org.bukkit.*;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import rip.battle.crates.Crates;
+import rip.battle.crates.airdrop.Airdrop;
+import rip.battle.crates.misterybox.MysteryBox;
+import rip.battle.crates.reward.Reward;
+import rip.battle.crates.reward.RewardType;
+import rip.battle.crates.supplydrop.SupplyDrop;
+import rip.battle.crates.utils.ChatUtils;
+import rip.battle.crates.utils.RandomUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Setter
+@Getter
+public class Crate {
+
+ private static final MongoCollection collection = Crates.getInstance().getMongoDatabase().getCollection("crates");
+
+ @Getter
+ private static final Map crates = Maps.newHashMap();
+
+ private final String name;
+ private boolean enable;
+
+ private List rewards = Lists.newArrayList();
+ private List placeholders = Lists.newArrayList();
+
+ private String displayName;
+
+ private ItemStack key;
+
+ private Sound openSound;
+
+ private int minimumReward = 1;
+ private int maximumReward = 2;
+
+ private Location chestLocation;
+
+ private final Map holograms = Maps.newHashMap();
+
+ private final Inventory inventory;
+
+ public Crate(String name) {
+ this.name = name;
+ this.enable = true;
+
+ this.displayName = ChatColor.GOLD + name;
+
+ this.key = new ItemBuilder(Material.TRIPWIRE_HOOK).name(CC.translate(displayName + " Key")).build();
+ this.inventory = Bukkit.createInventory(null, 54, CC.translate("&eEdit Rewards of " + name));
+
+ if (!name.equalsIgnoreCase("Airdrop")) {
+ crates.put(name, this);
+ }
+ }
+
+ public void destroyHolograms() {
+ holograms.values().forEach(Hologram::destroy);
+ holograms.clear();
+ }
+
+ public List getHologramsLines() {
+ return Arrays.asList(
+ displayName + " Crate",
+ "&7",
+ "&fLeft click&7 &7for preview rewards",
+ "&fRight click&7 to open",
+ "&7",
+ "&7&ostore.battle.rip");
+ }
+
+ public void sendHolograms() {
+ if (chestLocation == null) return;
+ double y = 0.50;
+
+ if (chestLocation.getBlock().getType() == Material.BEACON || chestLocation.getBlock().getType() == Material.ENDER_CHEST
+ || chestLocation.getBlock().getType() == Material.ENDER_PORTAL || chestLocation.getBlock().getType() == Material.DROPPER) {
+ y = 0.90;
+ }
+
+ Location finalLocation = chestLocation.clone().add(0.50, y, 0.50);
+ Hologram hologram = Holograms.newHologram().at(finalLocation).addLines(getHologramsLines()).build();
+ hologram.send();
+ this.holograms.put(finalLocation, hologram);
+ }
+
+ public void createHologram(Location location) {
+ Location finalLocation = location.clone().add(0.50, 0.50, 0.50);
+ Hologram hologram = Holograms.newHologram().at(finalLocation).addLines(getHologramsLines()).build();
+ hologram.send();
+ this.holograms.put(finalLocation, hologram);
+ }
+
+ public static void load() {
+ Crate crate;
+ for (Document document : collection.find()) {
+
+ if (document.containsKey("type")) {
+ if (document.getString("type").equalsIgnoreCase("supplydrop")) {
+ crate = new SupplyDrop();
+ } else if (document.getString("type").equalsIgnoreCase("mysterybox")) {
+ crate = new MysteryBox(document.getString("name"));
+ } else if (document.getString("type").equalsIgnoreCase("airdrop")) {
+ crate = new Airdrop();
+ } else {
+ crate = new Crate(document.getString("name"));
+ }
+ } else {
+ crate = new Crate(document.getString("name"));
+ }
+
+ crate.setEnable(document.getBoolean("enable"));
+ crate.setDisplayName(document.getString("displayName"));
+ crate.setKey(CorePlugin.GSON.fromJson(document.getString("key"), ItemStack.class));
+ if (document.containsKey("openSound")) {
+ crate.setOpenSound(Sound.valueOf(document.getString("openSound")));
+ }
+ crate.setMinimumReward(document.getInteger("minimumReward"));
+ crate.setMaximumReward(document.getInteger("maximumReward"));
+
+ for (Document reward : document.getList("rewards", Document.class)) {
+ crate.getRewards().add(new Reward(reward));
+ }
+
+ for (Document placeholder : document.getList("placeholders", Document.class)) {
+ crate.getPlaceholders().add(new CratePlaceholder(placeholder));
+ }
+
+ if (crate instanceof MysteryBox) {
+ for (Document reward : document.getList("obligatoryrewards", Document.class)) {
+ ((MysteryBox) crate).getObligatoryRewards().add(new Reward(reward));
+ }
+ }
+
+ if (document.containsKey("chestLocations")) {
+ for (String location : document.getList("chestLocations", String.class)) {
+ crate.setChestLocation(LocationUtil.convertLocation(location));
+ }
+ }
+
+ if (document.containsKey("chestLocation")) {
+ crate.setChestLocation(LocationUtil.convertLocation(document.getString("chestLocation")));
+ }
+
+ for (CratePlaceholder placeholder : crate.getPlaceholders()) {
+ crate.getInventory().setItem(placeholder.getSlot(), placeholder.getItem());
+ }
+
+ for (Reward reward : crate.getRewards()) {
+ crate.getInventory().setItem(reward.getSlot(), reward.getItem());
+ }
+
+ if (crate instanceof MysteryBox) {
+ for (Reward reward : ((MysteryBox) crate).getObligatoryRewards()) {
+ crate.getInventory().setItem(reward.getSlot(), reward.getItem());
+ }
+ }
+
+ crate.sendHolograms();
+ }
+
+ if (!crates.containsKey("Airdrop")) {
+ crates.put("Airdrop", new Airdrop());
+ }
+
+ if (!crates.containsKey("SupplyDrop")) {
+ crates.put("SupplyDrop", new SupplyDrop());
+ }
+ }
+
+ public void save() {
+ Document document = new Document();
+
+ document.append("name", name);
+ document.append("enable", enable);
+ document.append("displayName", displayName);
+ document.append("key", CorePlugin.GSON.toJson(key));
+
+ if (openSound != null) document.append("openSound", openSound.name());
+
+ document.append("minimumReward", minimumReward);
+ document.append("maximumReward", maximumReward);
+ document.append("rewards", rewards.stream().map(Reward::serialize).collect(Collectors.toList()));
+ document.append("placeholders", placeholders.stream().map(CratePlaceholder::serialize).collect(Collectors.toList()));
+ //document.append("chestLocations", chestLocations.stream().map(LocationUtil::parseLocation).collect(Collectors.toList()));
+
+ if (chestLocation != null) {
+ document.append("chestLocation", LocationUtil.parseLocation(chestLocation));
+ }
+
+ if (this instanceof MysteryBox) {
+ MysteryBox mysteryBox = (MysteryBox) this;
+ document.append("type", "mysteryBox");
+ document.append("obligatoryrewards", mysteryBox.getObligatoryRewards().stream().map(Reward::serialize).collect(Collectors.toList()));
+ } else if (this instanceof Airdrop) {
+ document.append("type", "airdrop");
+ } else if (this instanceof SupplyDrop) {
+ document.append("type", "supplydrop");
+ } else {
+ document.append("type", "crate");
+ }
+
+ collection.replaceOne(Filters.eq("name", name), document, new ReplaceOptions().upsert(true));
+ }
+
+ public static Crate getByLocation(Location location) {
+ for (Crate crate : crates.values()) {
+ if (crate.getChestLocation() != null && crate.getChestLocation().equals(location)) {
+ return crate;
+ }
+ }
+
+ return null;
+ }
+
+
+ public static Crate getByName(String name) {
+ return crates.get(name);
+ }
+
+ public ItemStack generateKey() {
+
+ if (getName().equalsIgnoreCase("Giftbox")) {
+ return new ItemBuilder(key.clone())
+ .name(CC.translate(displayName + " &7(Package) &a&l* CLICK *"))
+ .setLore(Lists.newArrayList(
+ "&7",
+ "&7You can preview this " + getDisplayName() + " Crate &7 rewards",
+ "&7By going to the overworld &aSpawn",
+ "&7",
+ ChatUtils.getFirstColor(getDisplayName()) + "Right Click &7to open the key",
+ "&7",
+ "&7Purchase additional keys at &f&nstore.battle.rip"
+ ))
+ .build();
+ }
+
+ return new ItemBuilder(key.clone())
+ .name(CC.translate(displayName + " Key"))
+ .setLore(Lists.newArrayList(
+ "&7",
+ "&7You can preview this " + getDisplayName() + " Crate &7 rewards",
+ "&7By going to the overworld &aSpawn",
+ "&7",
+ ChatUtils.getFirstColor(getDisplayName()) + "Right Click &7to open the key",
+ ChatUtils.getFirstColor(getDisplayName()) + "Left Click &7to preview rewards",
+ "&7",
+ "&7Purchase additional keys at &f&nstore.battle.rip"
+ ))
+ .build();
+ }
+
+ public static Crate getCrateByKey(ItemStack key) {
+
+ if (key == null) return null;
+
+ if (key.getItemMeta() == null) return null;
+
+ ItemMeta meta = key.getItemMeta();
+ if (meta.hasDisplayName() && meta.hasLore()) {
+ String name = meta.getDisplayName();
+
+ if (name.contains(CC.translate("&7(Package)"))) {
+ return Crate.getByName("Giftbox");
+ }
+
+ if (name.contains("Key")) {
+ name = name.replace(" Key", "");
+
+ String finalName = name;
+
+ return crates.values()
+ .stream()
+ .filter(crate -> crate.getDisplayName().equalsIgnoreCase(finalName))
+ .findFirst().orElse(null);
+ }
+ }
+
+ return null;
+ }
+
+ public void openCrate(Player player) {
+
+ if (!enable) {
+ player.sendMessage(CC.translate("&cThis crate is currently disabled"));
+ return;
+ }
+
+ if (getMaximumReward() == 0 || getRewards().isEmpty()) {
+ player.sendMessage(CC.translate("&cCrate " + getName() + " is empty, please contact an admin."));
+ return;
+ }
+
+ if (player.getInventory().firstEmpty() < 0) {
+ player.sendMessage(CC.translate("&cInventory Full."));
+ return;
+ }
+
+ int random = new Random().nextInt(getMaximumReward() - getMinimumReward() + 1) + getMinimumReward();
+
+ List randomRewards = new ArrayList<>();
+
+ List rewardList = new ArrayList<>(getRewards());
+ Collections.shuffle(rewardList);
+
+ for (int i = 0; i < random; i++) {
+ randomRewards.add(RandomUtils.getRandomReward(rewardList));
+ }
+
+ for (Reward reward : randomRewards) {
+ if (reward.getType() == RewardType.ITEMS) {
+ if (player.getInventory().firstEmpty() < 0) {
+ player.sendMessage(CC.translate("&cInventory Full."));
+ player.getWorld().dropItem(player.getLocation(), reward.getItem());
+ } else {
+ player.getInventory().addItem(reward.getItem());
+ }
+ } else {
+ List commands = reward.getCommands();
+
+ for (String command : commands) {
+
+ if (command.contains("op")) {
+ continue;
+ }
+
+ Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command.replace("%player%", player.getName()));
+ }
+ }
+
+ if (!reward.getBroadcast().isEmpty()) {
+ reward.getBroadcast().forEach(line -> Bukkit.broadcastMessage(line.replace("{player}", player.getName())));
+ }
+ }
+
+ if (randomRewards.size() == 0) { // if not found any reward
+ Reward reward = getRewards().get(new Random().nextInt(getRewards().size()));
+
+ if (reward.getType() == RewardType.ITEMS) {
+ if (player.getInventory().firstEmpty() < 0) {
+ player.sendMessage(CC.translate("&cInventory Full."));
+ player.getWorld().dropItem(player.getLocation(), reward.getItem());
+ } else {
+ player.getInventory().addItem(reward.getItem());
+ }
+ } else {
+ List commands = reward.getCommands();
+
+ for (String command : commands) {
+ Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command.replace("%player%", player.getName()));
+ }
+ }
+
+ if (!reward.getBroadcast().isEmpty()) {
+ reward.getBroadcast().forEach(line -> Bukkit.broadcastMessage(line.replace("{player}", player.getName())));
+ }
+ }
+
+ if (openSound != null) {
+ player.playSound(player.getLocation(), openSound, 1, 1);
+ }
+
+ player.sendMessage(CC.translate("&aYou have received " + randomRewards.size() + " reward(s)"));
+ consumeKey(player);
+ player.updateInventory();
+ }
+
+ public void openEditRewardsInventory(Player player) {
+ player.openInventory(inventory);
+ }
+
+ public void consumeKey(Player player) {
+ if (player.getItemInHand().getAmount() > 1) {
+ player.getItemInHand().setAmount(player.getItemInHand().getAmount() - 1);
+ } else {
+ player.setItemInHand(new ItemStack(Material.AIR));
+ }
+ }
+
+ public Reward getReward(int slot) {
+ return rewards.stream().filter(reward -> reward.getSlot() == slot).findFirst().orElse(null);
+ }
+
+ public CratePlaceholder getPlaceholder(int slot) {
+ return placeholders.stream().filter(reward -> reward.getSlot() == slot).findFirst().orElse(null);
+ }
+
+ public void delete() {
+ crates.remove(name);
+ destroyHolograms();
+
+ collection.deleteOne(Filters.eq("name", name));
+ }
+
+ public void removeHologram() {
+ holograms.forEach((loc, hologram) -> hologram.destroy());
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/crate/CratePlaceholder.java b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/CratePlaceholder.java
new file mode 100644
index 0000000..a16030b
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/CratePlaceholder.java
@@ -0,0 +1,32 @@
+package rip.battle.crates.crate;
+
+import cc.stormworth.core.CorePlugin;
+import cc.stormworth.core.kt.util.ItemBuilder;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.bson.Document;
+import org.bukkit.inventory.ItemStack;
+
+@Getter
+@AllArgsConstructor
+public class CratePlaceholder {
+
+ private ItemStack item;
+ private int slot;
+
+ public CratePlaceholder(Document document) {
+ this.item = CorePlugin.GSON.fromJson(document.getString("item"), ItemStack.class);
+ this.slot = document.getInteger("slot");
+ }
+
+ public ItemStack getPreview() {
+ return new ItemBuilder(item.clone())
+ .name(" ")
+ .build();
+ }
+
+ public Document serialize(){
+ return new Document("item", CorePlugin.GSON.toJson(item))
+ .append("slot", slot);
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/crate/commands/CrateCommands.java b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/commands/CrateCommands.java
new file mode 100644
index 0000000..b98743f
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/commands/CrateCommands.java
@@ -0,0 +1,286 @@
+package rip.battle.crates.crate.commands;
+
+import cc.stormworth.core.util.chat.CC;
+import cc.stormworth.core.util.chat.Clickable;
+import cc.stormworth.core.util.command.annotations.Command;
+import cc.stormworth.core.util.command.annotations.Param;
+import cc.stormworth.core.util.onedoteight.TitleBuilder;
+import cc.stormworth.hcf.profile.HCFProfile;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.block.Block;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import rip.battle.crates.airdrop.Airdrop;
+import rip.battle.crates.crate.Crate;
+import rip.battle.crates.crate.menus.CSGOOpenMenu;
+import rip.battle.crates.crate.menus.CrateEditMenu;
+import rip.battle.crates.crate.menus.CratePreviewMenu;
+import rip.battle.crates.misterybox.MysteryBox;
+
+public class CrateCommands {
+
+ @Command(names = {"crate", "cr", "crates", "mysterybox", "mb"}, permission = "PLATFORMADMINISTRATOR", hidden = true)
+ public static void help(Player player) {
+ player.sendMessage(CC.translate("&a&lCrates Help"));
+ player.sendMessage(CC.translate("&7- &e/crate create &7- &aCreate a crate"));
+ player.sendMessage(CC.translate("&7- &e/mystery create &7- &aCreate a mystery box"));
+ player.sendMessage("");
+ player.sendMessage(CC.translate("&7- &e/crate delete &7- &aDelete a crate"));
+ player.sendMessage(CC.translate("&7- &e/crate list &7- &aList all crates"));
+ player.sendMessage(CC.translate("&7- &e/crate edit &7- &aEdit a crate"));
+ player.sendMessage(CC.translate("&7- &e/crate preview &7- &aPreview a crate"));
+ player.sendMessage(CC.translate("&7- &e/crate give &7- &aGive a crate key to player"));
+ player.sendMessage(CC.translate("&7- &e/reward &7- &aTo show help for rewards"));
+
+ player.sendMessage(CC.translate("&7- &e/airdrop give &7- &aTo show help for rewards"));
+ }
+
+ @Command(names = {"airdrop"}, permission = "", hidden = true)
+ public static void airdrop(Player player) {
+ Airdrop airdrop = (Airdrop) Crate.getByName("Airdrop");
+
+ if (airdrop == null) {
+ player.sendMessage(CC.translate("&cairdrop with that name does not exist!"));
+ return;
+ }
+
+ new CratePreviewMenu(airdrop).openMenu(player);
+ }
+
+ @Command(names = {"crate create", "cr create", "crates create"}, permission = "PLATFORMADMINISTRATOR", hidden = true)
+ public static void create(Player player, @Param(name = "name") String name) {
+ if (Crate.getByName(name) != null) {
+ player.sendMessage(CC.translate("&cCrate with that name already exists!"));
+ return;
+ }
+
+ Crate crate = new Crate(name);
+
+ crate.save();
+
+ player.sendMessage(CC.translate("&aCrate &e" + name + "&a created!"));
+ }
+
+ @Command(names = {"mysterybox create", "mb create"}, permission = "PLATFORMADMINISTRATOR", hidden = true)
+ public static void mysterybox(Player player, @Param(name = "name") String name) {
+ if (Crate.getByName(name) != null) {
+ player.sendMessage(CC.translate("&cCrate with that name already exists!"));
+ return;
+ }
+
+ MysteryBox crate = new MysteryBox(name);
+
+ crate.save();
+
+ player.sendMessage(CC.translate("&aMystery &e" + name + "&a created!"));
+ }
+
+ @Command(names = {"crate delete", "cr delete", "crates delete", "crate remove", "cr remove", "crates remove"}, permission = "PLATFORMADMINISTRATOR", hidden = true)
+ public static void delete(Player player, @Param(name = "name") String name) {
+ Crate crate = Crate.getByName(name);
+ if (crate == null) {
+ player.sendMessage(CC.translate("&cCrate with that name does not exist!"));
+ return;
+ }
+
+ crate.delete();
+ player.sendMessage(CC.translate("&aCrate &e" + name + "&a deleted!"));
+ }
+
+ @Command(names = {"crate list", "cr list", "crates list", "mysterybox list", "mb list"}, permission = "PLATFORMADMINISTRATOR", hidden = true)
+ public static void list(Player player) {
+ player.sendMessage(CC.translate("&aCrates:"));
+ for (Crate crate : Crate.getCrates().values()) {
+ if (crate instanceof MysteryBox) {
+ player.sendMessage(CC.translate("&7- &e" + crate.getName() + " &7- &aMystery Box"));
+ } else if (crate instanceof Airdrop) {
+ player.sendMessage(CC.translate("&7- &e" + crate.getName() + " &7- &aAirdrop"));
+ } else {
+ player.sendMessage(CC.translate("&7- &e" + crate.getName() + " &7- &aCrate"));
+ }
+ }
+ }
+
+ @Command(names = {"crate edit", "cr edit", "crates edit", "mysterybox edit", "mb edit", "airdrop edit", "ad edit"}, permission = "PLATFORMADMINISTRATOR", hidden = true)
+ public static void edit(Player player, @Param(name = "name") String name) {
+ Crate crate = Crate.getByName(name);
+ if (crate == null) {
+ player.sendMessage(CC.translate("&cCrate with that name does not exist!"));
+ return;
+ }
+
+ new CrateEditMenu(crate).openMenu(player);
+ }
+
+ @Command(names = {"crate test"}, permission = "op", hidden = true)
+ public static void test(Player player, @Param(name = "name") String name) {
+ Crate crate = Crate.getByName(name);
+ if (crate == null) {
+ player.sendMessage(CC.translate("&cCrate with that name does not exist!"));
+ return;
+ }
+
+ new CSGOOpenMenu(crate).openMenu(player);
+ }
+
+ @Command(names = {"airdrop edit", "ad edit"}, permission = "PLATFORMADMINISTRATOR", hidden = true)
+ public static void airdropEdit(Player player) {
+ Crate crate = Crate.getByName("Airdrop");
+
+ if (crate == null) {
+ player.sendMessage(CC.translate("&cCrate with that name does not exist!"));
+ return;
+ }
+
+ new CrateEditMenu(crate).openMenu(player);
+ }
+
+ @Command(names = {"crate preview", "cr preview", "crates preview", "mysterybox preview", "mb preview"}, permission = "PLATFORMADMINISTRATOR", hidden = true)
+ public static void preview(Player player, @Param(name = "name") String name) {
+ Crate crate = Crate.getByName(name);
+ if (crate == null) {
+ player.sendMessage(CC.translate("&cCrate with that name does not exist!"));
+ return;
+ }
+
+ new CratePreviewMenu(crate).openMenu(player);
+ }
+
+ @Command(names = {"crate give", "cr give", "crates give", "mysterybox give", "mb give", "airdrop give", "ad give"}, permission = "PLATFORMADMINISTRATOR", hidden = true)
+ public static void give(CommandSender player, @Param(name = "name") String name,
+ @Param(name = "amount") int amount,
+ @Param(name = "target") Player target) {
+
+ Crate crate = Crate.getByName(name);
+
+ if (crate == null) {
+ player.sendMessage(CC.translate("&cCrate with that name does not exist!"));
+ return;
+ }
+
+ ItemStack item = crate.generateKey().clone();
+ item.setAmount(amount);
+ target.getInventory().addItem(item);
+
+ if (crate instanceof MysteryBox) {
+ player.sendMessage(CC.translate("&eYou have given x" + amount + " &6Mistery&e Keys to &6" + target.getName() + "&e!"));
+ } else if (crate instanceof Airdrop) {
+ player.sendMessage(CC.translate("&eYou have given x" + amount + " &6Airdrop&e to &6" + target.getName() + "&e!"));
+ } else {
+ player.sendMessage(CC.translate("&eYou have given x" + amount + " &6Crate Keys&e to &6" + target.getName() + "&e!"));
+ }
+ }
+
+ @Command(names = {"givekey", "crate givekey"}, permission = "PLATFORMADMINISTRATOR", hidden = true)
+ public static void give(CommandSender player,
+ @Param(name = "target") Player target,
+ @Param(name = "name") String name,
+ @Param(name = "amount") int amount) {
+ Crate crate = Crate.getByName(name);
+ if (crate == null) {
+ player.sendMessage(CC.translate("&cCrate with that name does not exist!"));
+ return;
+ }
+
+ ItemStack item = crate.generateKey().clone();
+ item.setAmount(amount);
+
+ if (target.getInventory().firstEmpty() == -1) {
+ target.sendMessage("");
+ target.sendMessage(CC.translate("&a&eYour inventory was &cfull&e."));
+
+ Clickable clickable = new Clickable("&a[Click here] &eto recover the items.",
+ "&a[Click here]",
+ "/reclaimitems");
+ clickable.sendToPlayer(target);
+ target.sendMessage("");
+
+ HCFProfile profile = HCFProfile.get(target);
+
+ profile.getNoReclaimedItems().add(Crate.getByName("Blood").generateKey());
+ return;
+ }
+
+ target.getInventory().addItem(item);
+ }
+
+ @Command(names = {"crate setchest", "cr setchest", "crates setchest", "mysterybox setchest", "mb setchest"}, permission = "PLATFORMADMINISTRATOR", hidden = true)
+ public static void setChest(Player player, @Param(name = "name") String name) {
+ Crate crate = Crate.getByName(name);
+
+ if (crate == null) {
+ player.sendMessage(CC.translate("&cCrate with that name does not exist!"));
+ return;
+ }
+
+ Block targetBlock = player.getTargetBlock(null, 4);
+
+ if (targetBlock == null) {
+ player.sendMessage(CC.translate("&cYou must be look a block."));
+ return;
+ }
+
+ Location location = targetBlock.getLocation();
+ if (crate.getChestLocation() != null && crate.getChestLocation().equals(location)) {
+ player.sendMessage(CC.translate("&cThere is another crate."));
+ return;
+ }
+
+ crate.createHologram(location);
+
+ crate.setChestLocation(location);
+ player.sendMessage(CC.translate("&aSuccessfully set chest location."));
+ }
+
+ @Command(names = {"keyall"}, permission = "PLATFORMADMINISTRATOR", hidden = true)
+ public static void keyall(CommandSender sender, @Param(name = "name") String name, @Param(name = "amount") int amount) {
+ Crate crate = Crate.getByName(name);
+
+ if (crate == null) {
+ sender.sendMessage(CC.translate("&cCrate with that name does not exist!"));
+ return;
+ }
+
+ for (Player target : Bukkit.getOnlinePlayers()) {
+ ItemStack item = crate.generateKey().clone();
+ item.setAmount(amount);
+ if (target.getInventory().firstEmpty() != -1) {
+ target.getInventory().addItem(item);
+ } else {
+ target.getWorld().dropItem(target.getLocation(), item);
+ }
+
+ TitleBuilder titleBuilder = new TitleBuilder(crate.getDisplayName() + " &areceived.",
+ "&eAmount: &f" + amount,
+ 10, 20, 10);
+ titleBuilder.send(target);
+ }
+
+ sender.sendMessage(CC.translate("&eYou have given &6x" + amount + " " + crate.getName() + " &eto &6everybody&e."));
+ }
+
+ @Command(names = {"keyalldone"}, permission = "PLATFORMADMINISTRATOR", hidden = true)
+ public static void keyalldone(CommandSender sender) {
+
+ for (Player other : Bukkit.getOnlinePlayers()) {
+ TitleBuilder titleBuilder = new TitleBuilder("&4&lKeyall", "&7has finished.", 10, 20, 10);
+ titleBuilder.send(other);
+ }
+ }
+
+ @Command(names = {"create removechest", "cr removechest"}, permission = "PLATFORMADMINISTRATOR", hidden = true)
+ public static void removeLocation(CommandSender sender, @Param(name = "name") String name) {
+ Crate crate = Crate.getByName(name);
+
+ if (crate == null) {
+ sender.sendMessage(CC.translate("&cCrate with that name does not exist!"));
+ return;
+ }
+
+ crate.setChestLocation(null);
+ crate.removeHologram();
+ sender.sendMessage(CC.translate("&aSuccessfully removed chest location."));
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/crate/listeners/CrateListener.java b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/listeners/CrateListener.java
new file mode 100644
index 0000000..1d4ce72
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/listeners/CrateListener.java
@@ -0,0 +1,189 @@
+package rip.battle.crates.crate.listeners;
+
+import cc.stormworth.core.util.chat.CC;
+import cc.stormworth.core.util.general.TaskUtil;
+import com.google.common.collect.Lists;
+import org.bukkit.GameMode;
+import org.bukkit.Location;
+import org.bukkit.Sound;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.inventory.InventoryCloseEvent;
+import org.bukkit.event.player.PlayerInteractEvent;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import rip.battle.crates.Crates;
+import rip.battle.crates.airdrop.Airdrop;
+import rip.battle.crates.crate.Crate;
+import rip.battle.crates.crate.CratePlaceholder;
+import rip.battle.crates.crate.menus.CSGOOpenMenu;
+import rip.battle.crates.crate.menus.CrateEditMenu;
+import rip.battle.crates.crate.menus.CratePreviewMenu;
+import rip.battle.crates.misterybox.MysteryBox;
+import rip.battle.crates.reward.Reward;
+import rip.battle.crates.supplydrop.SupplyDrop;
+import rip.battle.crates.utils.RandomUtils;
+
+import java.util.List;
+
+public class CrateListener implements Listener {
+
+ @EventHandler
+ public void onInventoryClose(InventoryCloseEvent event) {
+ Player player = (Player) event.getPlayer();
+
+ Inventory inventory = event.getInventory();
+
+ if (player.hasMetadata("closeByRightClick")) {
+ player.removeMetadata("closeByRightClick", Crates.getInstance());
+ return;
+ }
+
+ if (inventory.getName().contains(CC.translate("&eEdit Rewards of "))) {
+
+ String name = inventory.getName().replace(CC.translate("&eEdit Rewards of "), "");
+
+ Crate crate = Crate.getByName(name);
+
+ if (crate == null) {
+ return;
+ }
+
+ List placeholders = Lists.newArrayList();
+
+ for (int i = 0; i < 54; i++) {
+ ItemStack item = inventory.getItem(i);
+
+ if (item == null || item.getType() == null) {
+ continue;
+ }
+
+ if (crate.getReward(i) != null) {
+ continue;
+ }
+
+ if (crate instanceof MysteryBox) {
+ MysteryBox box = (MysteryBox) crate;
+
+ if (box.getObligatoryReward(i) != null) {
+ continue;
+ }
+ }
+
+ placeholders.add(new CratePlaceholder(item, i));
+ }
+
+ crate.setPlaceholders(placeholders);
+
+ player.sendMessage(CC.translate("&aSuccessfully updated the rewards of &e" + name + "&a!"));
+ player.playSound(player.getLocation(), Sound.LEVEL_UP, 0.5F, 0.5F);
+
+ TaskUtil.runAsync(Crates.getInstance(), crate::save);
+
+ TaskUtil.runLater(Crates.getInstance(), () -> new CrateEditMenu(crate).openMenu(player),
+ 2L);
+ }
+ }
+
+ @EventHandler
+ public void onPlayerInteract(PlayerInteractEvent event) {
+ Player player = event.getPlayer();
+
+ if (Crate.getCrateByKey(player.getItemInHand()) instanceof Airdrop) {
+ player.sendMessage(CC.translate("&cTo use AirDrop you have to put the block on the ground."));
+ return;
+ }
+
+ if (Crate.getCrateByKey(player.getItemInHand()) instanceof SupplyDrop) {
+ return;
+ }
+
+ if (Crate.getCrateByKey(player.getItemInHand()) instanceof MysteryBox) {
+ MysteryBox box = (MysteryBox) Crate.getCrateByKey(player.getItemInHand());
+ box.openCrate(player);
+ return;
+ }
+
+ if (Crate.getCrateByKey(player.getItemInHand()) != null) {
+
+ Crate crate = Crate.getCrateByKey(player.getItemInHand());
+
+ if (crate.getName().equalsIgnoreCase("Giftbox")) {
+ crate.consumeKey(player);
+ if (player.isSneaking()) {
+
+ if (player.getInventory().firstEmpty() == -1) {
+ player.sendMessage(CC.translate("&cYour inventory is full!"));
+ return;
+ }
+
+ Reward reward = RandomUtils.getRandomReward(crate.getRewards());
+
+ player.getInventory().addItem(reward.getItem());
+ } else {
+ new CSGOOpenMenu(crate).openMenu(player);
+ }
+ return;
+ }
+
+ event.setCancelled(true);
+
+ if (event.getAction().name().contains("RIGHT_CLICK")) {
+ if (Crate.getCrateByKey(player.getItemInHand()) == crate) {
+ crate.openCrate(player);
+ return;
+ }
+
+ if (player.isSneaking() && player.getGameMode() == GameMode.CREATIVE && player.isOp()) {
+ new CrateEditMenu(crate).openMenu(player);
+ return;
+ }
+
+ new CratePreviewMenu(crate).openMenu(player);
+ }
+ if (event.getAction().name().contains("LEFT_CLICK")) {
+ if (player.isSneaking() && player.getGameMode() == GameMode.CREATIVE && player.isOp()) {
+ new CrateEditMenu(crate).openMenu(player);
+ return;
+ }
+
+ new CratePreviewMenu(crate).openMenu(player);
+ }
+ }
+
+ if (event.getClickedBlock() == null) return;
+
+ Location location = event.getClickedBlock().getLocation();
+
+ Crate crate = Crate.getByLocation(location);
+
+ if (crate == null) {
+ return;
+ }
+
+ event.setCancelled(true);
+
+ if (event.getAction().name().contains("RIGHT_CLICK")) {
+ if (Crate.getCrateByKey(player.getItemInHand()) == crate) {
+ crate.openCrate(player);
+ return;
+ }
+
+ if (player.isSneaking() && player.getGameMode() == GameMode.CREATIVE && player.isOp()) {
+ new CrateEditMenu(crate).openMenu(player);
+ return;
+ }
+
+ new CratePreviewMenu(crate).openMenu(player);
+ }
+ if (event.getAction().name().contains("LEFT_CLICK")) {
+ if (player.isSneaking() && player.getGameMode() == GameMode.CREATIVE && player.isOp()) {
+ new CrateEditMenu(crate).openMenu(player);
+ return;
+ }
+
+ new CratePreviewMenu(crate).openMenu(player);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/crate/menus/CSGOOpenMenu.java b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/menus/CSGOOpenMenu.java
new file mode 100644
index 0000000..0c1f6ff
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/menus/CSGOOpenMenu.java
@@ -0,0 +1,92 @@
+package rip.battle.crates.crate.menus;
+
+import cc.stormworth.core.kt.util.ItemBuilder;
+import cc.stormworth.core.menu.Button;
+import cc.stormworth.core.menu.Menu;
+import com.google.common.collect.Maps;
+import org.bukkit.Material;
+import org.bukkit.Sound;
+import org.bukkit.entity.Player;
+import rip.battle.crates.crate.Crate;
+import rip.battle.crates.reward.Reward;
+import rip.battle.crates.utils.RandomUtils;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class CSGOOpenMenu extends Menu {
+
+ private final Crate crate;
+ private final Reward randomReward;
+ private final List rewards;
+ int time = 0;
+ private boolean claimed = false;
+
+ @Override
+ public String getTitle(Player player) {
+ return crate.getDisplayName() + " Crate";
+ }
+
+ public CSGOOpenMenu(Crate crate) {
+ this.crate = crate;
+ this.rewards = crate.getRewards();
+
+ randomReward = RandomUtils.getRandomReward(rewards);
+
+ Collections.shuffle(rewards);
+ setAutoUpdate(true);
+ setUpdateAfterClick(false);
+ }
+
+ @Override
+ public void onClose(Player player) {
+ if (!claimed) {
+ player.getInventory().addItem(randomReward.getItem());
+ player.updateInventory();
+ player.playSound(player.getLocation(), Sound.LEVEL_UP, 1.0F, 1.0F);
+ claimed = true;
+ }
+ }
+
+ @Override
+ public Map getButtons(Player player) {
+ Map buttons = Maps.newHashMap();
+
+ Collections.rotate(rewards, 1);
+
+ for (int i = 0; i < 9; i++) {
+ buttons.put(buttons.size(), Button.fromItem(new ItemBuilder(Material.STAINED_GLASS_PANE).data((short) 0).build()));
+ }
+
+ for (int i = 0; i < 9; i++) {
+ buttons.put(buttons.size(), Button.fromItem(rewards.get(i).getItem()));
+ }
+
+ for (int i = 18; i < 27; i++) {
+ buttons.put(buttons.size(), Button.fromItem(new ItemBuilder(Material.STAINED_GLASS_PANE).data((short) 0).build()));
+ }
+
+ buttons.put(4, Button.fromItem(new ItemBuilder(Material.STAINED_GLASS_PANE).data((short) 5).build()));
+ buttons.put(22, Button.fromItem(new ItemBuilder(Material.STAINED_GLASS_PANE).data((short) 5).build()));
+
+ player.playSound(player.getLocation(), Sound.NOTE_STICKS, 1.0F, 1.0F);
+
+ Button button = buttons.get(13);
+
+ if (time == 25) {
+ if (button.getButtonItem(player).isSimilar(randomReward.getItem())) {
+ setAutoUpdate(false);
+
+ player.getInventory().addItem(randomReward.getItem());
+ player.updateInventory();
+ player.playSound(player.getLocation(), Sound.LEVEL_UP, 1.0F, 1.0F);
+ claimed = true;
+ }
+ } else {
+ time++;
+ }
+
+ return buttons;
+ }
+}
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/crate/menus/CrateBroadcastMenu.java b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/menus/CrateBroadcastMenu.java
new file mode 100644
index 0000000..b6939ac
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/menus/CrateBroadcastMenu.java
@@ -0,0 +1,109 @@
+package rip.battle.crates.crate.menus;
+
+import cc.stormworth.core.kt.util.ItemBuilder;
+import cc.stormworth.core.menu.Button;
+import cc.stormworth.core.menu.Menu;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import rip.battle.crates.crate.Crate;
+import rip.battle.crates.crate.prompt.CrateBroadCastPrompt;
+import rip.battle.crates.crate.prompt.CrateEditLinePrompt;
+import rip.battle.crates.reward.Reward;
+import rip.battle.crates.reward.menus.RewardEditMenu;
+import rip.battle.crates.utils.ChatUtils;
+
+public class CrateBroadcastMenu extends Menu {
+
+ private final Crate crate;
+ private final Reward reward;
+
+ public CrateBroadcastMenu(Crate crate, Reward reward) {
+ this.crate = crate;
+ this.reward = reward;
+
+ setUpdateAfterClick(true);
+ }
+
+ @Override
+ public String getTitle(Player player) {
+ return "&eBroadcast Lines";
+ }
+
+ @Override
+ public Map getButtons(Player player) {
+
+ Map buttons = Maps.newHashMap();
+
+ buttons.put(0, Button.fromItem(new ItemBuilder(Material.BED)
+ .name("&cGo Back").build(),
+ (other) -> new RewardEditMenu(reward, crate).openMenu(other)));
+
+ buttons.put(4, Button.fromItem(new ItemBuilder(Material.BOOK)
+ .name("&aAdd new lines").build(),
+ (other) -> ChatUtils.beginPrompt(other, new CrateBroadCastPrompt(crate, reward))));
+
+ buttons.put(8, Button.fromItem(new ItemBuilder(Material.JUKEBOX)
+ .name("&eClick to preview broadcast").build(),
+ (other) -> {
+
+ player.closeInventory();
+
+ LinkedList lines = reward.getBroadcast();
+
+ if (lines.isEmpty()) {
+ other.sendMessage("&cNo broadcast lines set!");
+ }else{
+ lines.forEach(line -> other.sendMessage(line.replace("{player}", other.getName())));
+ }
+ }));
+
+ int i = 9;
+ int index = 0;
+ for (String line : reward.getBroadcast()) {
+ buttons.put(i++, new BroadcastLineButton(line, index++));
+ }
+
+ return buttons;
+ }
+
+ @RequiredArgsConstructor
+ public class BroadcastLineButton extends Button{
+
+ private final String line;
+ private final int index;
+
+ @Override
+ public String getName(Player player) {
+ return line;
+ }
+
+ @Override
+ public List getDescription(Player player) {
+ return Lists.newArrayList(
+ "&fRight &7click to Delete this line",
+ "&fLeft &7click to Edit this line"
+ );
+ }
+
+ @Override
+ public Material getMaterial(Player player) {
+ return Material.PAPER;
+ }
+
+ @Override
+ public void clicked(Player player, int slot, ClickType clickType) {
+ if (clickType == ClickType.RIGHT) {
+ reward.getBroadcast().remove(line);
+ } else if (clickType == ClickType.LEFT) {
+ ChatUtils.beginPrompt(player, new CrateEditLinePrompt(crate, reward, index));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/crate/menus/CrateDeleteMenu.java b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/menus/CrateDeleteMenu.java
new file mode 100644
index 0000000..5691862
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/menus/CrateDeleteMenu.java
@@ -0,0 +1,52 @@
+package rip.battle.crates.crate.menus;
+
+import cc.stormworth.core.kt.util.ItemBuilder;
+import cc.stormworth.core.menu.Button;
+import cc.stormworth.core.menu.Menu;
+import cc.stormworth.core.util.chat.CC;
+import com.google.common.collect.Maps;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import rip.battle.crates.crate.Crate;
+
+import java.util.Map;
+
+@RequiredArgsConstructor
+public class CrateDeleteMenu extends Menu {
+
+ private final Crate crate;
+
+ @Override
+ public String getTitle(Player player) {
+ return "&cDelete Crate or chest";
+ }
+
+ @Override
+ public Map getButtons(Player player) {
+ Map buttons = Maps.newHashMap();
+
+ buttons.put(0, Button.fromItem(new ItemBuilder(Material.BED)
+ .name("&cGo Back").build(),
+ (other) -> new CrateEditMenu(crate).openMenu(other)));
+
+ buttons.put(getSlot(3, 1), Button.fromItem(new ItemBuilder(Material.FLINT_AND_STEEL).name("&cDelete Crate").build(),
+ (other) -> {
+ other.closeInventory();
+ crate.delete();
+ }));
+
+ buttons.put(getSlot(5, 1),
+ Button.fromItem(new ItemBuilder(Material.CHEST)
+ .name("&cDelete Chest").build(), (other) -> {
+
+
+ crate.removeHologram();
+ crate.setChestLocation(null);
+ player.sendMessage(CC.translate("&aSuccessfully removed chest location."));
+ other.closeInventory();
+ }));
+
+ return buttons;
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/crate/menus/CrateEditMenu.java b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/menus/CrateEditMenu.java
new file mode 100644
index 0000000..9bf09fd
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/menus/CrateEditMenu.java
@@ -0,0 +1,190 @@
+package rip.battle.crates.crate.menus;
+
+import cc.stormworth.core.kt.util.ItemBuilder;
+import cc.stormworth.core.menu.Button;
+import cc.stormworth.core.menu.Menu;
+import cc.stormworth.core.util.chat.CC;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import java.util.List;
+import java.util.Map;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import rip.battle.crates.crate.Crate;
+import rip.battle.crates.crate.prompt.CrateKeyItemPrompt;
+import rip.battle.crates.crate.prompt.CrateRenamePrompt;
+import rip.battle.crates.utils.ChatUtils;
+
+public class CrateEditMenu extends Menu {
+
+ private final Crate crate;
+
+ public CrateEditMenu(Crate crate) {
+ this.crate = crate;
+
+ setUpdateAfterClick(true);
+ }
+
+ @Override
+ public String getTitle(Player player) {
+ String name = "&eEditing &a" + crate.getDisplayName() + "&e...";
+
+ if(name.length() > 32) {
+ name = name.substring(0, 32);
+ }
+
+ return name;
+ }
+
+ @Override
+ public Map getButtons(Player player) {
+ Map buttons = Maps.newHashMap();
+
+
+ buttons.put(getSlot(2, 1), Button.fromItem(
+ new ItemBuilder(Material.NAME_TAG).name("&6Change display name")
+ .addToLore(
+ "",
+ "&7Click to change displayname."
+ ).build(),
+ (other) -> ChatUtils.beginPrompt(other, new CrateRenamePrompt(crate))));
+
+ buttons.put(getSlot(4, 1), Button.fromItem(
+ new ItemBuilder(Material.CHEST)
+ .name("&6Edit rewards")
+ .addToLore(
+ "",
+ "&7Click to edit the rewards for this crate."
+ ).build(),
+ crate::openEditRewardsInventory));
+
+ buttons.put(getSlot(6, 1), Button.fromItem(
+ new ItemBuilder(crate.getKey().getType())
+ .data(crate.getKey().getDurability())
+ .name("&6Change key item")
+ .addToLore(
+ "",
+ "&7Click to change the key item for this crate."
+ ).build(),
+ (other) -> ChatUtils.beginPrompt(other, new CrateKeyItemPrompt(crate))));
+
+
+ buttons.put(getSlot(4, 2), Button.fromItem(
+ new ItemBuilder(Material.JUKEBOX).name("&6Select open sound")
+ .addToLore(
+ "",
+ "&7Click to select the sound that plays when this crate is opened."
+ )
+ .build(),
+ (other) -> new SelectSoundMenu(crate).openMenu(player)));
+
+ buttons.put(getSlot(2, 3), new MinAmountButton());
+
+ buttons.put(getSlot(4, 3), Button.fromItem(
+ new ItemBuilder(crate.isEnable() ? Material.REDSTONE_TORCH_ON : Material.LEVER)
+ .name(crate.isEnable() ? "&cDisable crate" : "&aEnable crate")
+ .addToLore(
+ "",
+ "&7Click to " + (crate.isEnable() ? "disable" : "enable") + " this crate."
+ ).build(),
+ (other) -> crate.setEnable(!crate.isEnable())
+ ));
+
+ buttons.put(getSlot(6, 3), new MaxRewardsButton());
+
+
+ buttons.put(getSlot(5, 5), Button.fromItem(
+ new ItemBuilder(Material.FLINT_AND_STEEL).name("&4&lDelete crate")
+ .addToLore(
+ "",
+ "&4&lClick to delete this crate."
+ ).build(), (other) -> new CrateDeleteMenu(crate).openMenu(other)));
+
+ buttons.put(getSlot(3, 5), Button.fromItem(
+ new ItemBuilder(Material.ENDER_CHEST).name("&6Preview rewards")
+ .addToLore(
+ "",
+ "&7Click to open preview menu of rewards for this crate."
+ )
+ .build(), (other) -> new CratePreviewMenu(crate).openMenu(other)));
+
+ return buttons;
+ }
+
+ public class MinAmountButton extends Button{
+
+ @Override
+ public String getName(Player player) {
+ return "&6Select Min rewards amount";
+ }
+
+ @Override
+ public List getDescription(Player player) {
+ return Lists.newArrayList(
+ "&7Minimum amount of rewards to be given: &f" + crate.getMinimumReward(),
+ "",
+ "&fRight click &7to decrease",
+ "&fLeft click &7to increase",
+ ""
+ );
+ }
+
+ @Override
+ public Material getMaterial(Player player) {
+ return Material.WOOD_BUTTON;
+ }
+
+ @Override
+ public void clicked(Player player, int slot, ClickType clickType) {
+ if(clickType == ClickType.RIGHT) {
+ if(crate.getMinimumReward() == 1) {
+ player.sendMessage(CC.translate("&cCrates with a minimum reward of 1 are not allowed."));
+ Button.playFail(player);
+ return;
+ }
+ crate.setMinimumReward(crate.getMinimumReward() - 1);
+ } else if(clickType == ClickType.LEFT) {
+ crate.setMinimumReward(crate.getMinimumReward() + 1);
+ }
+ }
+ }
+
+ public class MaxRewardsButton extends Button{
+
+ @Override
+ public String getName(Player player) {
+ return "&6Select Max rewards amount";
+ }
+
+ @Override
+ public List getDescription(Player player) {
+ return Lists.newArrayList(
+ "&7Maximum amount of rewards to be given: &f" + crate.getMaximumReward(),
+ "",
+ "&fRight click &7to decrease",
+ "&fLeft click &7to increase",
+ ""
+ );
+ }
+
+ @Override
+ public Material getMaterial(Player player) {
+ return Material.STONE_BUTTON;
+ }
+
+ @Override
+ public void clicked(Player player, int slot, ClickType clickType) {
+ if(clickType == ClickType.RIGHT) {
+ if(crate.getMaximumReward() <= crate.getMinimumReward()) {
+ player.sendMessage(CC.translate("&cMaximum reward must be greater than or equal to minimum reward."));
+ Button.playFail(player);
+ return;
+ }
+ crate.setMaximumReward(crate.getMaximumReward() - 1);
+ } else if(clickType == ClickType.LEFT) {
+ crate.setMaximumReward(crate.getMaximumReward() + 1);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/crate/menus/CratePreviewMenu.java b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/menus/CratePreviewMenu.java
new file mode 100644
index 0000000..498f99f
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/menus/CratePreviewMenu.java
@@ -0,0 +1,48 @@
+package rip.battle.crates.crate.menus;
+
+import cc.stormworth.core.menu.Button;
+import cc.stormworth.core.menu.Menu;
+import com.google.common.collect.Maps;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import net.md_5.bungee.api.ChatColor;
+import org.bukkit.entity.Player;
+import rip.battle.crates.crate.Crate;
+import rip.battle.crates.crate.CratePlaceholder;
+import rip.battle.crates.misterybox.MysteryBox;
+
+@RequiredArgsConstructor
+public class CratePreviewMenu extends Menu {
+
+ private final Crate crate;
+
+ @Override
+ public String getTitle(Player player) {
+ String name = crate.getName();
+
+ if(name.length() > 32) {
+ name = name.substring(0, 32);
+ }
+
+ return ChatColor.GOLD + "Preview " + name;
+ }
+
+ @Override
+ public Map getButtons(Player player) {
+ Map buttons = Maps.newHashMap();
+
+ for (CratePlaceholder placeholder : crate.getPlaceholders()) {
+ if (placeholder.getItem() != null) {
+ buttons.put(placeholder.getSlot(), Button.fromItem(placeholder.getPreview()));
+ }
+ }
+
+ crate.getRewards().forEach(reward -> buttons.put(reward.getSlot(), Button.fromItem(reward.getItem())));
+
+ if(crate instanceof MysteryBox) {
+ ((MysteryBox) crate).getObligatoryRewards().forEach(reward -> buttons.put(reward.getSlot(), Button.fromItem(reward.getItem())));
+ }
+
+ return buttons;
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/crate/menus/SelectSoundMenu.java b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/menus/SelectSoundMenu.java
new file mode 100644
index 0000000..ca16207
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/menus/SelectSoundMenu.java
@@ -0,0 +1,96 @@
+package rip.battle.crates.crate.menus;
+
+import cc.stormworth.core.kt.util.ItemBuilder;
+import cc.stormworth.core.menu.Button;
+import cc.stormworth.core.menu.Menu;
+import cc.stormworth.core.menu.pagination.PaginatedMenu;
+import cc.stormworth.core.util.chat.CC;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import java.util.List;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.Material;
+import org.bukkit.Sound;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import rip.battle.crates.crate.Crate;
+
+@RequiredArgsConstructor
+public class SelectSoundMenu extends PaginatedMenu {
+
+ private final Crate crate;
+
+ @Override
+ public String getTitle(Player player) {
+ return "&6Select sound when open";
+ }
+
+ @Override
+ public String getPrePaginatedTitle(Player player) {
+ return "&6Select sound when open &f" + crate.getName();
+ }
+
+ @Override
+ public int getMaxItemsPerPage(Player player) {
+ return 27;
+ }
+
+ @Override
+ public Map getGlobalButtons(Player player) {
+ Map buttons = Maps.newHashMap();
+
+ buttons.put(4, Button.fromItem(new ItemBuilder(Material.BED).name("&cGo Back").build(),
+ (other) -> new CrateEditMenu(crate).openMenu(other)));
+
+ return buttons;
+ }
+
+ @Override
+ public Map getAllPagesButtons(Player player) {
+ Map buttons = Maps.newHashMap();
+
+ for (Sound sound : Sound.values()) {
+ buttons.put(buttons.size(), new SoundButton(sound));
+ }
+
+ return buttons;
+ }
+
+ @RequiredArgsConstructor
+ public class SoundButton extends Button{
+ private final Sound sound;
+
+ @Override
+ public String getName(Player player) {
+ return "&6" + sound.name();
+ }
+
+ @Override
+ public List getDescription(Player player) {
+ return Lists.newArrayList(
+ "&fLeft click &6to Set sound",
+ "&fRight click &7to Play sound"
+ );
+ }
+
+ @Override
+ public Material getMaterial(Player player) {
+ return Material.JUKEBOX;
+ }
+
+ @Override
+ public void clicked(Player player, int slot, ClickType clickType) {
+ if(clickType == ClickType.LEFT){
+ player.playSound(player.getLocation(), sound, 1, 1);
+ crate.setOpenSound(sound);
+
+ player.sendMessage(CC.translate("&6Set sound to &f" + sound.name()));
+
+ new CrateEditMenu(crate).openMenu(player);
+ }else{
+ player.playSound(player.getLocation(), sound, 1, 1);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/crate/prompt/CrateBroadCastPrompt.java b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/prompt/CrateBroadCastPrompt.java
new file mode 100644
index 0000000..01d2fec
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/prompt/CrateBroadCastPrompt.java
@@ -0,0 +1,49 @@
+package rip.battle.crates.crate.prompt;
+
+import cc.stormworth.core.util.chat.CC;
+import java.util.LinkedList;
+import org.bukkit.conversations.ConversationContext;
+import org.bukkit.conversations.Prompt;
+import org.bukkit.conversations.StringPrompt;
+import org.bukkit.entity.Player;
+import rip.battle.crates.crate.Crate;
+import rip.battle.crates.crate.menus.CrateBroadcastMenu;
+import rip.battle.crates.reward.Reward;
+
+public class CrateBroadCastPrompt extends StringPrompt {
+
+ private final Crate crate;
+ private final Reward reward;
+ private final LinkedList lines;
+
+ public CrateBroadCastPrompt(Crate crate, Reward reward) {
+ this.reward = reward;
+ this.crate = crate;
+ this.lines = reward.getBroadcast();
+ }
+
+ @Override
+ public String getPromptText(ConversationContext context) {
+ return CC.translate("&7Enter a lines to broadcast when the crate is opened. "
+ + "&7&oOr type &ccancel&7&o to cancel or type &aconfirm&7 to end: ");
+ }
+
+ @Override
+ public Prompt acceptInput(ConversationContext context, String input) {
+
+ Player player = (Player) context.getForWhom();
+
+ if (input.equalsIgnoreCase("cancel") || input.equalsIgnoreCase("confirm")) {
+
+ reward.setBroadcast(lines);
+
+ new CrateBroadcastMenu(crate, reward).openMenu(player);
+ return Prompt.END_OF_CONVERSATION;
+ }
+
+ lines.add(CC.translate(input.replace("{name}", crate.getDisplayName())));
+
+ player.sendMessage(CC.translate("&aAdded new broadcast line"));
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/crate/prompt/CrateEditLinePrompt.java b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/prompt/CrateEditLinePrompt.java
new file mode 100644
index 0000000..dba9d09
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/prompt/CrateEditLinePrompt.java
@@ -0,0 +1,40 @@
+package rip.battle.crates.crate.prompt;
+
+import cc.stormworth.core.util.chat.CC;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.conversations.ConversationContext;
+import org.bukkit.conversations.Prompt;
+import org.bukkit.conversations.StringPrompt;
+import org.bukkit.entity.Player;
+import rip.battle.crates.crate.Crate;
+import rip.battle.crates.crate.menus.CrateBroadcastMenu;
+import rip.battle.crates.reward.Reward;
+
+@RequiredArgsConstructor
+public class CrateEditLinePrompt extends StringPrompt {
+
+ private final Crate crate;
+ private final Reward reward;
+ private final int index;
+
+ @Override
+ public String getPromptText(ConversationContext conversationContext) {
+ return CC.translate("&7Enter a text to modify the line number &6" + index + "&7. Or type &ccancel&7 to cancel.");
+ }
+
+ @Override
+ public Prompt acceptInput(ConversationContext context, String input) {
+ Player player = (Player) context.getForWhom();
+
+ if (input.equalsIgnoreCase("cancel")) {
+ new CrateBroadcastMenu(crate, reward).openMenu(player);
+ return Prompt.END_OF_CONVERSATION;
+ }
+
+ reward.getBroadcast().set(index, CC.translate(input.replace("{name}", crate.getDisplayName())));
+
+ new CrateBroadcastMenu(crate, reward).openMenu(player);
+
+ return Prompt.END_OF_CONVERSATION;
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/crate/prompt/CrateKeyItemPrompt.java b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/prompt/CrateKeyItemPrompt.java
new file mode 100644
index 0000000..3b9c578
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/prompt/CrateKeyItemPrompt.java
@@ -0,0 +1,48 @@
+package rip.battle.crates.crate.prompt;
+
+import cc.stormworth.core.util.chat.CC;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.conversations.ConversationContext;
+import org.bukkit.conversations.Prompt;
+import org.bukkit.conversations.StringPrompt;
+import org.bukkit.entity.Player;
+import rip.battle.crates.crate.Crate;
+import rip.battle.crates.crate.menus.CrateEditMenu;
+
+@RequiredArgsConstructor
+public class CrateKeyItemPrompt extends StringPrompt {
+
+ private final Crate crate;
+
+ @Override
+ public String getPromptText(ConversationContext conversationContext) {
+ return CC.translate("&7Put item in your hand and type &6accept&7 to confirm. Or type &6cancel&7 to cancel.");
+ }
+
+ @Override
+ public Prompt acceptInput(ConversationContext context, String input) {
+
+ Player player = (Player) context.getForWhom();
+
+ if (input.equalsIgnoreCase("cancel")) {
+ player.sendMessage(CC.translate("&cCrate rename cancelled."));
+ new CrateEditMenu(crate).openMenu(player);
+ return Prompt.END_OF_CONVERSATION;
+ }
+
+ if(player.getItemInHand() == null) {
+ player.sendMessage(CC.translate("&cYou must have an item in your hand to select it."));
+ return this;
+ }
+
+ if (input.equalsIgnoreCase("accept")) {
+ crate.setKey(player.getItemInHand());
+ player.sendMessage(CC.translate("&aCrate key item set to &6" + player.getItemInHand().getType().name() + "&a."));
+
+ new CrateEditMenu(crate).openMenu(player);
+ return Prompt.END_OF_CONVERSATION;
+ }
+
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/crate/prompt/CrateRenamePrompt.java b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/prompt/CrateRenamePrompt.java
new file mode 100644
index 0000000..e455a97
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/crate/prompt/CrateRenamePrompt.java
@@ -0,0 +1,43 @@
+package rip.battle.crates.crate.prompt;
+
+import cc.stormworth.core.util.chat.CC;
+import lombok.AllArgsConstructor;
+import org.bukkit.conversations.ConversationContext;
+import org.bukkit.conversations.Prompt;
+import org.bukkit.conversations.StringPrompt;
+import org.bukkit.entity.Player;
+import rip.battle.crates.crate.Crate;
+import rip.battle.crates.crate.menus.CrateEditMenu;
+
+@AllArgsConstructor
+public class CrateRenamePrompt extends StringPrompt {
+
+ private final Crate crate;
+
+ @Override
+ public String getPromptText(ConversationContext conversationContext) {
+ return CC.translate("&7Enter a new display name for the crate: &6" + crate.getName() + "&7. Or type &6cancel&7 to cancel.");
+ }
+
+ @Override
+ public Prompt acceptInput(ConversationContext context, String input) {
+
+ Player player = (Player) context.getForWhom();
+
+ if (input.equalsIgnoreCase("cancel")) {
+ player.sendMessage(CC.translate("&cCrate rename cancelled."));
+ new CrateEditMenu(crate).openMenu(player);
+ return Prompt.END_OF_CONVERSATION;
+ }
+
+ crate.setDisplayName(CC.translate(input));
+
+ player.sendMessage(CC.translate("&aCrate displayname change to &6" + crate.getDisplayName() + "&a."));
+
+ crate.destroyHolograms();
+ crate.sendHolograms();
+
+ new CrateEditMenu(crate).openMenu(player);
+ return Prompt.END_OF_CONVERSATION;
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/misterybox/MysteryBox.java b/Battle-Crates-main/src/main/java/rip/battle/crates/misterybox/MysteryBox.java
new file mode 100644
index 0000000..7b58ccb
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/misterybox/MysteryBox.java
@@ -0,0 +1,146 @@
+package rip.battle.crates.misterybox;
+
+import cc.stormworth.core.kt.util.ItemBuilder;
+import cc.stormworth.core.util.chat.CC;
+import com.google.common.collect.Lists;
+import lombok.Getter;
+import lombok.Setter;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import rip.battle.crates.crate.Crate;
+import rip.battle.crates.reward.Reward;
+import rip.battle.crates.reward.RewardType;
+import rip.battle.crates.utils.ChatUtils;
+import rip.battle.crates.utils.RandomUtils;
+
+import java.util.*;
+
+@Getter
+@Setter
+public class MysteryBox extends Crate {
+
+ private List obligatoryRewards = Lists.newArrayList();
+
+ public MysteryBox(String name) {
+ super(name);
+ }
+
+ @Override
+ public List getHologramsLines() {
+ return Arrays.asList(
+ getDisplayName() + " MisteryBox",
+ "&7",
+ "&fLeft click &7for preview rewards",
+ "&fRight click &7to open",
+ "&7",
+ "&7&ostore.battle.rip");
+ }
+
+ @Override
+ public ItemStack generateKey() {
+ return new ItemBuilder(getKey().clone())
+ .name(CC.translate(getDisplayName() + " Key"))
+ .setLore(Lists.newArrayList(
+ "&7",
+ "&7You can preview this " + getDisplayName() + " Crate &7 rewards",
+ "&7By going to the overworld &aSpawn",
+ "&7",
+ ChatUtils.getFirstColor(getDisplayName()) + "Right Click &7to open the key",
+ ChatUtils.getFirstColor(getDisplayName()) + "Left Click &7to preview rewards",
+ "&7",
+ "&7Purchase additional keys at &f&nstore.battle.rip"
+ ))
+ .build();
+ }
+
+ @Override
+ public void openCrate(Player player) {
+
+ if (!isEnable()) {
+ player.sendMessage(CC.translate("&cThis misterybox is currently disabled"));
+ return;
+ }
+
+ if (getMaximumReward() == 0 || getRewards().isEmpty()) {
+ player.sendMessage(CC.translate("&cCrate " + getName() + " is empty, please contact an admin."));
+ return;
+ }
+
+ if (player.getInventory().firstEmpty() < 0) {
+ player.sendMessage(CC.translate("&cInventory Full."));
+ return;
+ }
+
+ int random = new Random().nextInt(getMaximumReward() - getMinimumReward() + 1) + getMinimumReward();
+
+ List randomRewards = new ArrayList<>();
+
+ List rewardList = new ArrayList<>(getRewards());
+
+ Collections.shuffle(rewardList);
+
+ for (int i = 0; i < random; i++) {
+ randomRewards.add(RandomUtils.getRandomReward(rewardList));
+ }
+
+ for (Reward reward : obligatoryRewards) {
+ if (reward.getType() == RewardType.ITEMS) {
+ if (player.getInventory().firstEmpty() < 0) {
+ player.sendMessage(CC.translate("&cInventory Full."));
+ player.getWorld().dropItem(player.getLocation(), reward.getItem());
+ } else {
+ player.getInventory().addItem(reward.getItem());
+ }
+ } else {
+ List commands = reward.getCommands();
+
+ for (String command : commands) {
+ Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command.replace("%player%", player.getName()));
+ }
+ }
+
+ if (!reward.getBroadcast().isEmpty()) {
+ reward.getBroadcast().forEach(line -> Bukkit.broadcastMessage(line.replace("{player}", player.getName())));
+ }
+ }
+
+ for (Reward reward : randomRewards) {
+ if (reward.getType() == RewardType.ITEMS) {
+ if (player.getInventory().firstEmpty() < 0) {
+ player.sendMessage(CC.translate("&cInventory Full."));
+ player.getWorld().dropItem(player.getLocation(), reward.getItem());
+ } else {
+ player.getInventory().addItem(reward.getItem());
+ }
+ } else {
+ List commands = reward.getCommands();
+
+ for (String command : commands) {
+ if (command.contains("op")) {
+ continue;
+ }
+
+ Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command.replace("%player%", player.getName()));
+ }
+ }
+
+ if (!reward.getBroadcast().isEmpty()) {
+ reward.getBroadcast().forEach(line -> Bukkit.broadcastMessage(line.replace("{player}", player.getName())));
+ }
+ }
+
+
+ if (getOpenSound() != null) {
+ player.playSound(player.getLocation(), getOpenSound(), 1, 1);
+ }
+
+ player.sendMessage(CC.translate("&aYou have received " + (randomRewards.size() + obligatoryRewards.size()) + " reward(s)"));
+ consumeKey(player);
+ player.updateInventory();
+ }
+
+ public Reward getObligatoryReward(int slot) {
+ return obligatoryRewards.stream().filter(reward -> reward.getSlot() == slot).findFirst().orElse(null);
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/reward/Reward.java b/Battle-Crates-main/src/main/java/rip/battle/crates/reward/Reward.java
new file mode 100644
index 0000000..82b6ae9
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/reward/Reward.java
@@ -0,0 +1,152 @@
+package rip.battle.crates.reward;
+
+import cc.stormworth.core.CorePlugin;
+import cc.stormworth.core.kt.util.ItemBuilder;
+import cc.stormworth.core.util.chat.CC;
+import com.google.common.collect.Lists;
+import lombok.Getter;
+import lombok.Setter;
+import org.bson.Document;
+import org.bukkit.Material;
+import org.bukkit.inventory.ItemStack;
+import rip.battle.crates.utils.ItemUtils;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+@Getter
+@Setter
+public class Reward {
+
+ private ItemStack item;
+ private double chance;
+ private int slot;
+ private RewardType type;
+ private List commands;
+ private boolean obligatory;
+ private LinkedList broadcast = Lists.newLinkedList();
+
+ public Reward(ItemStack item, double chance, int slot, RewardType type, List commands, boolean obligatory) {
+ this.item = item;
+
+ if (this.item == null) {
+ this.item = new ItemStack(Material.AIR);
+ }
+
+ this.chance = chance;
+ this.slot = slot;
+ this.type = type;
+ this.commands = commands;
+ this.obligatory = obligatory;
+
+ if (this.chance > 1.0) {
+ this.chance = 1.0;
+ }
+
+ ItemUtils.removeRewardsLore(this.item);
+ }
+
+ public Reward(Document document) {
+ this.item = CorePlugin.GSON.fromJson(document.getString("item"), ItemStack.class);
+ this.slot = document.getInteger("slot");
+
+ if (document.get("chance") instanceof Double) {
+ this.chance = document.getDouble("chance");
+ } else if (document.get("chance") instanceof Integer) {
+ this.chance = Double.parseDouble(document.getInteger("chance").toString());
+ }
+
+ if (this.chance > 1.0) {
+ this.chance = 1.0;
+ }
+
+ if (document.containsKey("type")) {
+ this.type = RewardType.getByName(document.getString("type"));
+ } else {
+ this.type = RewardType.ITEMS;
+ }
+
+ if (document.containsKey("commands")) {
+ this.commands = document.getList("commands", String.class);
+ }
+
+ if (document.containsKey("broadcast")) {
+ setBroadcast(new LinkedList<>(document.getList("broadcast", String.class)));
+ }
+ }
+
+ public ItemStack getItem() {
+
+ if (this.item == null) {
+ return new ItemStack(Material.AIR);
+ }
+
+ ItemStack item = this.item.clone();
+
+ ItemUtils.removeRewardsLore(item);
+ return item;
+ }
+
+ public ItemStack getPreviewItem() {
+ List lore = Lists.newArrayList();
+
+ lore.add("&0");
+ lore.add("&7Type: &e" + this.type.name());
+ lore.add("&0");
+ lore.add("&7Chance: &e" + this.chance + "%");
+ if (this.commands != null && !this.commands.isEmpty()) {
+ lore.add("&7Commands: &e");
+ for (String command : this.commands) {
+ lore.add(" &7- &f" + command);
+ }
+ }
+ lore.add("&0");
+ lore.add("&fRight Click &7to edit");
+
+ return new ItemBuilder(this.item.clone())
+ .addLoreList(CC.translate(lore)).build();
+ }
+
+ public ItemStack getPreviewObligatoryItem() {
+ List lore = Lists.newArrayList();
+
+ lore.add("&0");
+ lore.add("&7Type: &e" + this.type.name());
+ lore.add("&0");
+ lore.add("&7Chance: &e" + this.chance + "%");
+ lore.add("&cObligatory");
+ if (this.commands != null && !this.commands.isEmpty()) {
+ lore.add("&7Commands: &e");
+ for (String command : this.commands) {
+ lore.add(" &7- &f" + command);
+ }
+ }
+ lore.add("&0");
+ lore.add("&fRight Click &7to edit");
+
+ return new ItemBuilder(this.item.clone())
+ .addLoreList(CC.translate(lore)).build();
+ }
+
+ public ItemStack getRealItem() {
+ return item;
+ }
+
+ public Document serialize() {
+ Document document = new Document();
+ document.append("item", CorePlugin.GSON.toJson(item));
+ document.append("slot", slot);
+ document.append("chance", chance);
+ document.append("type", type.name());
+
+ if (commands != null) {
+ document.append("commands", commands);
+ }
+
+ document.append("broadcast", new ArrayList<>(broadcast));
+
+ return document;
+ }
+
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/reward/RewardType.java b/Battle-Crates-main/src/main/java/rip/battle/crates/reward/RewardType.java
new file mode 100644
index 0000000..4814c7b
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/reward/RewardType.java
@@ -0,0 +1,17 @@
+package rip.battle.crates.reward;
+
+public enum RewardType {
+
+ COMMAND,
+ ITEMS;
+
+ public static RewardType getByName(String name) {
+ for (RewardType type : RewardType.values()) {
+ if (type.name().equalsIgnoreCase(name)) {
+ return type;
+ }
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/reward/RewardsListeners.java b/Battle-Crates-main/src/main/java/rip/battle/crates/reward/RewardsListeners.java
new file mode 100644
index 0000000..c8a8381
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/reward/RewardsListeners.java
@@ -0,0 +1,221 @@
+package rip.battle.crates.reward;
+
+import cc.stormworth.core.util.chat.CC;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryOpenEvent;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.metadata.FixedMetadataValue;
+import rip.battle.crates.Crates;
+import rip.battle.crates.crate.Crate;
+import rip.battle.crates.crate.CratePlaceholder;
+import rip.battle.crates.misterybox.MysteryBox;
+import rip.battle.crates.reward.menus.RewardEditMenu;
+import rip.battle.crates.utils.ItemUtils;
+
+public class RewardsListeners implements Listener {
+
+ @EventHandler
+ public void onInventoryOpen(InventoryOpenEvent event) {
+ Inventory inventory = event.getInventory();
+
+ if (inventory.getName().contains(CC.translate("&eEdit Rewards of "))) {
+
+ String name = inventory.getName().replace(CC.translate("&eEdit Rewards of "), "");
+
+ Crate crate = Crate.getByName(name);
+
+ if (crate == null) {
+ return;
+ }
+
+ for (CratePlaceholder placeholder : crate.getPlaceholders()) {
+ if (placeholder.getItem() == null) {
+ crate.getPlaceholders().remove(placeholder);
+ continue;
+ }
+
+ inventory.setItem(placeholder.getSlot(), placeholder.getItem());
+ }
+
+ crate.getRewards().forEach(reward -> inventory.setItem(reward.getSlot(), reward.getPreviewItem()));
+
+ if (crate instanceof MysteryBox) {
+ MysteryBox box = (MysteryBox) crate;
+ box.getObligatoryRewards().forEach(reward -> inventory.setItem(reward.getSlot(), reward.getPreviewObligatoryItem()));
+ }
+
+ }
+ }
+
+ @EventHandler
+ public void onInventoryClick(InventoryClickEvent event) {
+ Player player = (Player) event.getWhoClicked();
+
+ Inventory inventory = event.getInventory();
+
+ if (event.getClickedInventory() == null) {
+ return;
+ }
+
+ if (inventory.getName().contains(CC.translate("&eEdit Rewards of "))) {
+
+ String name = inventory.getName().replace(CC.translate("&eEdit Rewards of "), "");
+
+ Crate crate = Crate.getByName(name);
+
+ if (crate == null) {
+ return;
+ }
+
+ if (event.getClick() == ClickType.RIGHT) {
+
+ if (event.getClickedInventory() == player.getInventory()) {
+ return;
+ }
+
+ event.setCancelled(true);
+
+
+ if (crate instanceof MysteryBox) {
+ MysteryBox box = (MysteryBox) crate;
+
+ if (box.getObligatoryReward(event.getSlot()) != null) {
+ player.setMetadata("closeByRightClick", new FixedMetadataValue(Crates.getInstance(), true));
+
+ new RewardEditMenu(box.getObligatoryReward(event.getSlot()), crate).openMenu(player);
+ return;
+ }
+ }
+
+ if (crate.getReward(event.getSlot()) != null) {
+ player.setMetadata("closeByRightClick", new FixedMetadataValue(Crates.getInstance(), true));
+ new RewardEditMenu(crate.getReward(event.getSlot()), crate).openMenu(player);
+ } else if (crate.getPlaceholder(event.getSlot()) != null) {
+ CratePlaceholder placeholder = crate.getPlaceholder(event.getSlot());
+ Reward newReward = new Reward(placeholder.getItem().clone(), 100, placeholder.getSlot(), RewardType.ITEMS, null, false);
+
+ crate.getPlaceholders().remove(placeholder);
+ crate.getRewards().add(newReward);
+
+ player.setMetadata("closeByRightClick", new FixedMetadataValue(Crates.getInstance(), true));
+
+ new RewardEditMenu(newReward, crate).openMenu(player);
+ } else {
+ Reward newReward = new Reward(event.getCurrentItem(), 100, event.getSlot(), RewardType.ITEMS, null, false);
+
+ crate.getRewards().add(newReward);
+
+ player.setMetadata("closeByRightClick", new FixedMetadataValue(Crates.getInstance(), true));
+
+ new RewardEditMenu(newReward, crate).openMenu(player);
+ }
+ } else {
+ if (event.getClick() == ClickType.DROP || event.getClick() == ClickType.CONTROL_DROP) {
+ if (crate.getPlaceholder(event.getSlot()) != null) {
+ crate.getPlaceholders().remove(crate.getPlaceholder(event.getSlot()));
+ }
+
+ if (crate.getReward(event.getSlot()) != null) {
+ crate.getRewards().remove(crate.getReward(event.getSlot()));
+ }
+
+ if (crate instanceof MysteryBox) {
+ MysteryBox box = (MysteryBox) crate;
+
+ if (box.getObligatoryReward(event.getSlot()) != null) {
+ box.getObligatoryRewards().remove(box.getObligatoryReward(event.getSlot()));
+ }
+ }
+ } else if (event.getClick() == ClickType.SHIFT_LEFT) {
+ if (event.getClickedInventory() == player.getInventory()) {
+
+ if (ItemUtils.isReward(event.getCursor())) {
+ Reward reward = ItemUtils.getRewardByItem(event.getCurrentItem().clone(), inventory.firstEmpty());
+
+ if (reward != null) {
+ if (reward.isObligatory()) {
+ if (crate instanceof MysteryBox) {
+ MysteryBox box = (MysteryBox) crate;
+
+ box.getObligatoryRewards().add(reward);
+ return;
+ }
+ }
+
+ crate.getRewards().add(reward);
+
+ return;
+ }
+ }
+
+ crate.getPlaceholders().add(new CratePlaceholder(event.getCurrentItem().clone(), inventory.firstEmpty()));
+ } else {
+ if (crate.getPlaceholder(event.getSlot()) != null) {
+ crate.getPlaceholders().remove(crate.getPlaceholder(event.getSlot()));
+ }
+
+ if (crate.getReward(event.getSlot()) != null) {
+ crate.getRewards().remove(crate.getReward(event.getSlot()));
+ }
+
+ if (crate instanceof MysteryBox) {
+ MysteryBox box = (MysteryBox) crate;
+
+ if (box.getObligatoryReward(event.getSlot()) != null) {
+ box.getObligatoryRewards().remove(box.getObligatoryReward(event.getSlot()));
+ }
+ }
+ }
+ } else if (event.getClick() == ClickType.LEFT) {
+ if (event.getClickedInventory() == player.getInventory()) {
+ return;
+ }
+
+ if (event.getInventory().getItem(event.getSlot()) == null) {
+ if (ItemUtils.isReward(event.getCursor())) {
+ Reward reward = ItemUtils.getRewardByItem(event.getCursor().clone(), event.getSlot());
+
+ if (reward != null) {
+ if (reward.isObligatory()) {
+ if (crate instanceof MysteryBox) {
+ MysteryBox box = (MysteryBox) crate;
+
+ box.getObligatoryRewards().add(reward);
+ return;
+ }
+ }
+
+ crate.getRewards().add(reward);
+
+ return;
+ }
+ }
+
+ crate.getPlaceholders().add(new CratePlaceholder(event.getCursor().clone(), event.getSlot()));
+ } else {
+ if (crate.getPlaceholder(event.getSlot()) != null) {
+ crate.getPlaceholders().remove(crate.getPlaceholder(event.getSlot()));
+ }
+
+ if (crate.getReward(event.getSlot()) != null) {
+ crate.getRewards().remove(crate.getReward(event.getSlot()));
+ }
+
+ if (crate instanceof MysteryBox) {
+ MysteryBox box = (MysteryBox) crate;
+
+ if (box.getObligatoryReward(event.getSlot()) != null) {
+ box.getObligatoryRewards().remove(box.getObligatoryReward(event.getSlot()));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/reward/commands/RewardCommand.java b/Battle-Crates-main/src/main/java/rip/battle/crates/reward/commands/RewardCommand.java
new file mode 100644
index 0000000..1d9838b
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/reward/commands/RewardCommand.java
@@ -0,0 +1,115 @@
+package rip.battle.crates.reward.commands;
+
+import cc.stormworth.core.util.chat.CC;
+import cc.stormworth.core.util.command.annotations.Command;
+import cc.stormworth.core.util.command.annotations.Param;
+import com.google.common.collect.Lists;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+import java.util.List;
+
+public class RewardCommand {
+
+ @Command(names = {"reward"}, permission = "PLATFORMADMINISTRATOR")
+ public static void help(Player player) {
+ player.sendMessage(CC.translate("&a&lReward Help"));
+ player.sendMessage(CC.translate("&7- &e/reward setchance &7- &aSets the chance for the item in your hand"));
+ player.sendMessage(CC.translate("&7- &e/reward setobligatory &7- &aSets the item in your hand to be obligatory"));
+ player.sendMessage(CC.translate("&7- &e/reward addcommand &7- &aAdds a command to the item in your hand"));
+ }
+
+ @Command(names = {"reward setchance"}, permission = "PLATFORMADMINISTRATOR")
+ public static void setChance(Player player, @Param(name = "chance") int chance) {
+ if (chance < 0 || chance > 100) {
+ player.sendMessage(CC.translate("&cChance must be between 0 and 100!"));
+ return;
+ }
+
+ ItemStack itemStack = player.getItemInHand();
+
+ if (itemStack == null) {
+ player.sendMessage(CC.translate("&cYou must be holding an item to set the chance!"));
+ return;
+ }
+
+ ItemMeta meta = itemStack.getItemMeta();
+ if (meta == null) {
+ player.sendMessage(CC.translate("&cYou must be holding an item to set the chance!"));
+ return;
+ }
+
+ List lore = meta.getLore();
+
+ if (lore == null) {
+ lore = Lists.newArrayList();
+ }
+
+ lore.add(0, CC.translate("&7Chance:&a " + chance));
+
+ meta.setLore(lore);
+ itemStack.setItemMeta(meta);
+
+ player.sendMessage(CC.translate("&aSuccessfully set chance for &e" + itemStack.getType().name() + " &ato &e" + chance + "%"));
+ }
+
+ @Command(names = {"reward setobligatory"}, permission = "PLATFORMADMINISTRATOR")
+ public static void setObligatory(Player player) {
+ ItemStack itemStack = player.getItemInHand();
+
+ if (itemStack == null) {
+ player.sendMessage(CC.translate("&cYou must be holding an item to set the chance!"));
+ return;
+ }
+
+ ItemMeta meta = itemStack.getItemMeta();
+ if (meta == null) {
+ player.sendMessage(CC.translate("&cYou must be holding an item to set the chance!"));
+ return;
+ }
+
+ List lore = meta.getLore();
+
+ if (lore == null) {
+ lore = Lists.newArrayList();
+ }
+
+ lore.add(0, CC.translate("&7Obligatory"));
+
+ meta.setLore(lore);
+ itemStack.setItemMeta(meta);
+
+ player.sendMessage(CC.translate("&aSuccessfully set obligatory for &e" + itemStack.getType().name()));
+ }
+
+ @Command(names = {"reward addcommand"}, permission = "PLATFORMADMINISTRATOR")
+ public static void setcommand(Player player, @Param(name = "command", wildcard = true) String command) {
+ ItemStack itemStack = player.getItemInHand();
+
+ if (itemStack == null) {
+ player.sendMessage(CC.translate("&cYou must be holding an item to set the chance!"));
+ return;
+ }
+
+ ItemMeta meta = itemStack.getItemMeta();
+ if (meta == null) {
+ player.sendMessage(CC.translate("&cYou must be holding an item to set the chance!"));
+ return;
+ }
+
+ List lore = meta.getLore();
+
+ if (lore == null) {
+ lore = Lists.newArrayList();
+ }
+
+ lore.add(0, CC.translate("&7Command:&a " + command));
+
+ meta.setLore(lore);
+ itemStack.setItemMeta(meta);
+
+ player.sendMessage(CC.translate("&aSuccessfully added command for &e" + itemStack.getType().name()));
+ }
+
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/reward/menus/RewardCommandsMenu.java b/Battle-Crates-main/src/main/java/rip/battle/crates/reward/menus/RewardCommandsMenu.java
new file mode 100644
index 0000000..9a254c0
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/reward/menus/RewardCommandsMenu.java
@@ -0,0 +1,113 @@
+package rip.battle.crates.reward.menus;
+
+import cc.stormworth.core.kt.util.ItemBuilder;
+import cc.stormworth.core.menu.Button;
+import cc.stormworth.core.menu.Menu;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import java.util.List;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.ChatColor;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import rip.battle.crates.crate.Crate;
+import rip.battle.crates.reward.Reward;
+import rip.battle.crates.reward.RewardType;
+import rip.battle.crates.reward.promt.RewardAddCommandPrompt;
+import rip.battle.crates.utils.ChatUtils;
+
+public class RewardCommandsMenu extends Menu {
+
+ private final Reward reward;
+ private final Crate crate;
+
+ public RewardCommandsMenu(Reward reward, Crate crate) {
+ this.reward = reward;
+ this.crate = crate;
+
+ setUpdateAfterClick(true);
+ }
+
+ @Override
+ public String getTitle(Player player) {
+ return ChatColor.GREEN + "Manage commands";
+ }
+
+ @Override
+ public Map getButtons(Player player) {
+
+ Map buttons = Maps.newHashMap();
+
+
+ buttons.put(getSlot(0, 0), Button.fromItem(
+ new ItemBuilder(Material.BED)
+ .name("&cGo Back")
+ .addToLore(
+ "",
+ "&7Click to go back to the reward edit menu."
+ ).build(),
+ (other) -> {
+ if(reward.getCommands().isEmpty()) {
+ reward.setType(RewardType.ITEMS);
+ }
+
+ new RewardEditMenu(reward, crate).openMenu(other);
+ }));
+
+ if(reward.getCommands().isEmpty()) {
+ buttons.put(getSlot(4, 0), Button.fromItem(
+ new ItemBuilder(Material.PAPER)
+ .name("&bAdd Command")
+ .addToLore(
+ "",
+ "&7Click add command to reward."
+ ).build(),
+ (other) -> ChatUtils.beginPrompt(player, new RewardAddCommandPrompt(reward, crate, new RewardCommandsMenu(reward, crate)))));
+ }
+
+ int slot = 9;
+
+ for (String command : reward.getCommands()) {
+ buttons.put(slot, new CommandButton(command));
+ }
+
+ return buttons;
+ }
+
+ @RequiredArgsConstructor
+ public class CommandButton extends Button{
+
+ private final String command;
+
+ @Override
+ public String getName(Player player) {
+ return ChatColor.GOLD + command;
+ }
+
+ @Override
+ public List getDescription(Player player) {
+ return Lists.newArrayList(
+ "",
+ "&fLeft Click to edit command",
+ "&fRight Click to remove command"
+ );
+ }
+
+ @Override
+ public Material getMaterial(Player player) {
+ return Material.PAPER;
+ }
+
+ @Override
+ public void clicked(Player player, int slot, ClickType clickType) {
+ reward.getCommands().remove(command);
+ if (clickType == ClickType.LEFT) {
+ ChatUtils.beginPrompt(player, new RewardAddCommandPrompt(reward, crate, new RewardCommandsMenu(reward, crate)));
+ } else if (clickType == ClickType.RIGHT) {
+ Button.playNeutral(player);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/reward/menus/RewardEditMenu.java b/Battle-Crates-main/src/main/java/rip/battle/crates/reward/menus/RewardEditMenu.java
new file mode 100644
index 0000000..2fcda4a
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/reward/menus/RewardEditMenu.java
@@ -0,0 +1,143 @@
+package rip.battle.crates.reward.menus;
+
+import cc.stormworth.core.kt.util.ItemBuilder;
+import cc.stormworth.core.menu.Button;
+import cc.stormworth.core.menu.Menu;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import rip.battle.crates.Crates;
+import rip.battle.crates.crate.Crate;
+import rip.battle.crates.crate.menus.CrateBroadcastMenu;
+import rip.battle.crates.misterybox.MysteryBox;
+import rip.battle.crates.reward.Reward;
+import rip.battle.crates.reward.promt.RewardAddCommandPrompt;
+import rip.battle.crates.reward.promt.RewardSetChancePrompt;
+import rip.battle.crates.utils.ChatUtils;
+
+@RequiredArgsConstructor
+public class RewardEditMenu extends Menu {
+
+ private final Reward reward;
+ private final Crate crate;
+
+ @Override
+ public String getTitle(Player player) {
+ return "&eEditing reward...";
+ }
+
+ @Override
+ public int size(Map buttons) {
+ return 3 * 9;
+ }
+
+ @Override
+ public void onClose(Player player) {
+ if (player.hasMetadata("closeByRightClick")) {
+ player.removeMetadata("closeByRightClick", Crates.getInstance());
+ }
+ }
+
+ @Override
+ public Map getButtons(Player player) {
+ Map buttons = Maps.newHashMap();
+
+ buttons.put(getSlot(0, 0), Button.fromItem(
+ new ItemBuilder(Material.BED)
+ .name("&cGo Back")
+ .addToLore(
+ "",
+ "&7Click to go back to the reward edit menu."
+ ).build(),
+ (other) -> {
+ other.closeInventory();
+ crate.openEditRewardsInventory(other);
+ }));
+
+ buttons.put(getSlot(2, 1), Button.fromItem(
+ new ItemBuilder(Material.EXP_BOTTLE)
+ .name("&bSet Chance")
+ .addToLore(
+ "",
+ "&7Click to set the chance of this reward",
+ "",
+ "&7Current chance: &a" + reward.getChance() + "%"
+ ).build(),
+ (other) -> ChatUtils.beginPrompt(other, new RewardSetChancePrompt(reward, crate))
+ ));
+
+ if(crate instanceof MysteryBox){
+ MysteryBox box = (MysteryBox) crate;
+ buttons.put(getSlot(4, 1), Button.fromItem(
+ new ItemBuilder(Material.ANVIL)
+ .name(box.getObligatoryRewards().contains(reward) ? "&aObligatory" : "&cObligatory")
+ .addToLore(
+ "",
+ (box.getObligatoryRewards().contains(reward) ? "&cClick to unset" : "&aClick to set") + " &7as an obligatory reward"
+ ).build(),
+ (other) -> {
+ if(box.getObligatoryRewards().contains(reward)){
+ box.getObligatoryRewards().remove(reward);
+ box.getRewards().add(reward);
+ }else{
+ box.getObligatoryRewards().add(reward);
+ box.getRewards().remove(reward);
+ }
+
+ Button.playNeutral(other);
+ }
+ ));
+ }
+
+ buttons.put(getSlot(6, 1), new CommandButtons());
+
+ buttons.put(getSlot(4, 2),
+ Button.fromItem(new ItemBuilder(Material.PAPER)
+ .name("&6Broadcast Lines")
+ .addToLore("",
+ "&7Click to view broadcast lines").build(),
+ (other) -> new CrateBroadcastMenu(crate, reward).openMenu(other)));
+
+ return buttons;
+ }
+
+ public class CommandButtons extends Button{
+
+ @Override
+ public String getName(Player player) {
+ return "&bCommands";
+ }
+
+ @Override
+ public List getDescription(Player player) {
+ return Lists.newArrayList(
+ "",
+ "&fLeft Click to add a command to this reward",
+ "&fRight Click to manage commands",
+ "",
+ "&7Current Commands: &a" + (reward.getCommands() == null
+ || reward.getCommands().isEmpty() ? "None" : Arrays.toString(reward.getCommands().toArray()))
+ );
+ }
+
+ @Override
+ public Material getMaterial(Player player) {
+ return Material.BOOK;
+ }
+
+ @Override
+ public void clicked(Player player, int slot, ClickType clickType) {
+ if(clickType == ClickType.LEFT){
+ ChatUtils.beginPrompt(player, new RewardAddCommandPrompt(reward, crate, new RewardEditMenu(reward, crate)));
+ }else{
+ new RewardCommandsMenu(reward, crate).openMenu(player);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/reward/promt/RewardAddCommandPrompt.java b/Battle-Crates-main/src/main/java/rip/battle/crates/reward/promt/RewardAddCommandPrompt.java
new file mode 100644
index 0000000..d761a93
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/reward/promt/RewardAddCommandPrompt.java
@@ -0,0 +1,50 @@
+package rip.battle.crates.reward.promt;
+
+import cc.stormworth.core.menu.Menu;
+import cc.stormworth.core.util.chat.CC;
+import lombok.RequiredArgsConstructor;
+import net.minecraft.util.com.google.common.collect.Lists;
+import org.bukkit.conversations.ConversationContext;
+import org.bukkit.conversations.Prompt;
+import org.bukkit.conversations.StringPrompt;
+import org.bukkit.entity.Player;
+import rip.battle.crates.crate.Crate;
+import rip.battle.crates.reward.Reward;
+import rip.battle.crates.reward.menus.RewardEditMenu;
+import rip.battle.crates.reward.RewardType;
+
+@RequiredArgsConstructor
+public class RewardAddCommandPrompt extends StringPrompt {
+
+ private final Reward reward;
+ private final Crate crate;
+ private final Menu menu;
+
+ @Override
+ public String getPromptText(ConversationContext context) {
+ return CC.translate("&7Enter the command you want to add. Or type &6cancel&7 to go back.");
+ }
+
+ @Override
+ public Prompt acceptInput(ConversationContext context, String input) {
+
+ Player player = (Player) context.getForWhom();
+
+ if (input.equalsIgnoreCase("cancel")) {
+ new RewardEditMenu(reward, crate).openMenu(player);
+ return END_OF_CONVERSATION;
+ }
+
+ reward.setType(RewardType.COMMAND);
+
+ if(reward.getCommands() == null) {
+ reward.setCommands(Lists.newArrayList());
+ }
+
+ reward.getCommands().add(input);
+
+ menu.openMenu(player);
+ player.sendMessage(CC.translate("&aCommand &e" + input + "&e has been added to the reward."));
+ return END_OF_CONVERSATION;
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/reward/promt/RewardSetChancePrompt.java b/Battle-Crates-main/src/main/java/rip/battle/crates/reward/promt/RewardSetChancePrompt.java
new file mode 100644
index 0000000..597562c
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/reward/promt/RewardSetChancePrompt.java
@@ -0,0 +1,55 @@
+package rip.battle.crates.reward.promt;
+
+import cc.stormworth.core.util.chat.CC;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.conversations.ConversationContext;
+import org.bukkit.conversations.Prompt;
+import org.bukkit.conversations.StringPrompt;
+import org.bukkit.entity.Player;
+import rip.battle.crates.crate.Crate;
+import rip.battle.crates.reward.Reward;
+import rip.battle.crates.reward.menus.RewardEditMenu;
+
+@RequiredArgsConstructor
+public class RewardSetChancePrompt extends StringPrompt {
+
+ private final Reward reward;
+ private final Crate crate;
+
+ @Override
+ public String getPromptText(ConversationContext context) {
+ return CC.translate("&7Set the chance of this reward to be given. Or type &6cancel &7to go back.");
+ }
+
+ @Override
+ public Prompt acceptInput(ConversationContext context, String input) {
+
+ Player player = (Player) context.getForWhom();
+
+ if (input.equalsIgnoreCase("cancel")) {
+ new RewardEditMenu(reward, crate).openMenu(player);
+ return END_OF_CONVERSATION;
+ }
+
+ try {
+ double chance = Double.parseDouble(input);
+
+ if (chance < 0 || chance > 1.0) {
+ player.sendMessage(CC.translate("&cThe chance must be between 0 and 1.0."));
+ return this;
+ }
+
+ reward.setChance(chance);
+
+ player.sendMessage(CC.translate("&aThe chance of this reward is now &6" + chance + "&a%."));
+
+ new RewardEditMenu(reward, crate).openMenu(player);
+ } catch (NumberFormatException e) {
+ player.sendMessage(CC.translate("&cThe chance must be a number between 0 and 100."));
+ return this;
+ }
+
+
+ return END_OF_CONVERSATION;
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/supplydrop/SupplyDrop.java b/Battle-Crates-main/src/main/java/rip/battle/crates/supplydrop/SupplyDrop.java
new file mode 100644
index 0000000..8384920
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/supplydrop/SupplyDrop.java
@@ -0,0 +1,39 @@
+package rip.battle.crates.supplydrop;
+
+import cc.stormworth.core.kt.util.ItemBuilder;
+import org.bukkit.Material;
+import org.bukkit.inventory.ItemStack;
+import rip.battle.crates.crate.Crate;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class SupplyDrop extends Crate {
+
+ public SupplyDrop() {
+ super("SupplyDrop");
+ getCrates().put("SupplyDrop", this);
+ }
+
+ @Override
+ public List getHologramsLines() {
+ return Arrays.asList(
+ getDisplayName() + " SupplyDrop",
+ "&7",
+ "&fLeft click &7for preview rewards",
+ "&7",
+ "&7&ostore.battle.rip");
+ }
+
+ @Override
+ public ItemStack generateKey() {
+ return new ItemBuilder(Material.DROPPER)
+ .name("&4&lSupplyDrop")
+ .addToLore(
+ "&7Purchasable at &fstore.battle.rip&7.",
+ ""
+ ).build();
+ }
+
+
+}
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/utils/ChatUtils.java b/Battle-Crates-main/src/main/java/rip/battle/crates/utils/ChatUtils.java
new file mode 100644
index 0000000..1e68e16
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/utils/ChatUtils.java
@@ -0,0 +1,28 @@
+package rip.battle.crates.utils;
+
+import lombok.experimental.UtilityClass;
+import org.bukkit.ChatColor;
+import org.bukkit.conversations.ConversationFactory;
+import org.bukkit.conversations.StringPrompt;
+import org.bukkit.entity.Player;
+import rip.battle.crates.Crates;
+
+@UtilityClass
+public class ChatUtils {
+
+ public void beginPrompt(Player player, StringPrompt prompt) {
+ player.closeInventory();
+ player.beginConversation(
+ new ConversationFactory(Crates.getInstance())
+ .withFirstPrompt(prompt)
+ .withTimeout(60)
+ .withModality(false)
+ .withLocalEcho(false)
+ .buildConversation(player));
+ }
+
+ public String getFirstColor(String message) {
+ return ChatColor.translateAlternateColorCodes('&', message.substring(0, 2));
+ }
+
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/utils/FireworkUtil.java b/Battle-Crates-main/src/main/java/rip/battle/crates/utils/FireworkUtil.java
new file mode 100644
index 0000000..28c69ef
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/utils/FireworkUtil.java
@@ -0,0 +1,114 @@
+package rip.battle.crates.utils;
+
+import org.bukkit.Color;
+import org.bukkit.FireworkEffect;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_7_R4.entity.CraftFirework;
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.Firework;
+import org.bukkit.inventory.meta.FireworkMeta;
+
+import java.util.Random;
+
+public class FireworkUtil {
+
+ public static void launchFirework(Location loc) {
+ Firework fw = (Firework) loc.getWorld().spawnEntity(loc, EntityType.FIREWORK);
+ FireworkMeta fwm = fw.getFireworkMeta();
+
+ Random random = RandomUtils.getRandom();
+
+ int randomInt = random.nextInt(4) + 1;
+ FireworkEffect.Type type;
+
+ if (randomInt == 1) {
+ type = FireworkEffect.Type.BALL;
+ } else if (randomInt == 2) {
+ type = FireworkEffect.Type.BALL_LARGE;
+ } else if (randomInt == 3) {
+ type = FireworkEffect.Type.BURST;
+ } else {
+ type = FireworkEffect.Type.STAR;
+ }
+
+
+ int r1i = random.nextInt(17) + 1;
+ int r2i = random.nextInt(17) + 1;
+
+ Color c1 = getColor(r1i);
+ Color c2 = getColor(r2i);
+
+ FireworkEffect effect = FireworkEffect.builder()
+ .flicker(random.nextBoolean())
+ .withColor(c1)
+ .withFade(c2).with(type).trail(random.nextBoolean()).build();
+
+ fwm.addEffect(effect);
+
+ fwm.setPower(3);
+
+ ((CraftFirework) fw).getHandle().expectedLifespan = 5;
+
+ fw.setFireworkMeta(fwm);
+ }
+
+ public static Color getColor(int i) {
+ Color color = null;
+ switch (i) {
+ case 1:
+ color = Color.AQUA;
+ break;
+ case 2:
+ color = Color.BLACK;
+ break;
+ case 3:
+ color = Color.BLUE;
+ break;
+ case 4:
+ color = Color.FUCHSIA;
+ break;
+ case 5:
+ color = Color.GRAY;
+ break;
+ case 6:
+ color = Color.GREEN;
+ break;
+ case 7:
+ color = Color.LIME;
+ break;
+ case 8:
+ color = Color.MAROON;
+ break;
+ case 9:
+ color = Color.NAVY;
+ break;
+ case 10:
+ color = Color.OLIVE;
+ break;
+ case 11:
+ color = Color.ORANGE;
+ break;
+ case 12:
+ color = Color.PURPLE;
+ break;
+ case 13:
+ color = Color.RED;
+ break;
+ case 14:
+ color = Color.SILVER;
+ break;
+ case 15:
+ color = Color.TEAL;
+ break;
+ case 16:
+ color = Color.WHITE;
+ break;
+ case 17:
+ color = Color.YELLOW;
+ break;
+ default:
+ break;
+ }
+ return color;
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/utils/ItemUtils.java b/Battle-Crates-main/src/main/java/rip/battle/crates/utils/ItemUtils.java
new file mode 100644
index 0000000..f19c89e
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/utils/ItemUtils.java
@@ -0,0 +1,148 @@
+package rip.battle.crates.utils;
+
+import cc.stormworth.core.util.chat.CC;
+import com.google.common.collect.Lists;
+import lombok.experimental.UtilityClass;
+import org.bukkit.ChatColor;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import rip.battle.crates.reward.Reward;
+import rip.battle.crates.reward.RewardType;
+
+import java.util.List;
+
+@UtilityClass
+public class ItemUtils {
+
+ public boolean isReward(ItemStack item) {
+
+ if (item == null) return false;
+
+ if (!item.hasItemMeta()) return false;
+
+ if (!item.getItemMeta().hasLore()) return false;
+
+ return item.getItemMeta().getLore().stream().anyMatch(lore -> lore.contains(CC.translate("&7Type: &e")));
+ }
+
+ public void consume(Player player) {
+
+ ItemStack item = player.getInventory().getItemInHand();
+
+ if (item.getAmount() == 1) {
+ player.getInventory().remove(item);
+ } else {
+ item.setAmount(item.getAmount() - 1);
+ }
+
+ player.updateInventory();
+ }
+
+ public RewardType getRewardType(ItemStack item) {
+ if (!isReward(item)) return null;
+
+ for (String lore : item.getItemMeta().getLore()) {
+ if (lore.contains(CC.translate("&7Type: &e"))) {
+ return RewardType.getByName(ChatColor.stripColor(lore).replace(CC.translate("Type: "), "").toUpperCase());
+ }
+ }
+
+ return null;
+ }
+
+
+ public Reward getRewardByItem(ItemStack item, int slot) {
+ if (!isReward(item)) return null;
+
+ RewardType type = getRewardType(item);
+
+ if (type == null) return null;
+
+ double chance = getChance(item);
+
+ List commands = Lists.newArrayList();
+
+ if (type == RewardType.COMMAND) {
+ commands.addAll(getCommands(item));
+ }
+
+ return new Reward(item, chance, slot, type, commands, isObligatory(item));
+ }
+
+ public void removeRewardsLore(ItemStack item) {
+ ItemMeta meta = item.getItemMeta();
+
+ if (meta == null) return;
+
+ List lore = meta.getLore();
+
+ if (lore == null) return;
+
+ lore.removeIf(line -> line.contains(CC.translate("&7Type: &e")));
+ lore.removeIf(line -> line.contains(CC.translate("&7Chance: &e")));
+ lore.removeIf(line -> line.contains(CC.translate("&cObligatory")));
+ lore.removeIf(line -> line.contains(CC.translate("&7Commands: &e")));
+ lore.removeIf(line -> line.contains(CC.translate("&fRight Click &7to edit")));
+ lore.removeIf(line -> line.equalsIgnoreCase(CC.translate("&0")));
+
+ meta.setLore(lore);
+
+ item.setItemMeta(meta);
+ }
+
+ public double getChance(ItemStack item) {
+ if (!isReward(item)) return 0;
+ for (String lore : item.getItemMeta().getLore()) {
+ if (lore.contains(CC.translate("&7Chance: &e"))) {
+ return Double.parseDouble(ChatColor.stripColor(lore).replace(CC.translate("Chance: "), "").replace("%", ""));
+ }
+ }
+
+ return 0;
+ }
+
+ public boolean isObligatory(ItemStack item) {
+
+ if (item == null) return false;
+
+ if (!item.hasItemMeta()) return false;
+
+ if (!item.getItemMeta().hasLore()) return false;
+
+ return item.getItemMeta().getLore().stream().anyMatch(lore -> lore.contains(CC.translate("&cObligatory")));
+ }
+
+ public boolean isCommandType(ItemStack item) {
+
+ if (item == null) return false;
+
+ if (!item.hasItemMeta()) return false;
+
+ if (!item.getItemMeta().hasLore()) return false;
+
+ return item.getItemMeta().getLore().stream().anyMatch(lore -> lore.contains(CC.translate("&7Commands: &e")));
+ }
+
+ public List getCommands(ItemStack item) {
+ if (!isCommandType(item)) return null;
+
+ List commands = Lists.newArrayList();
+
+ for (String lore : item.getItemMeta().getLore()) {
+ if (lore.contains(CC.translate(" &7- &f"))) {
+ commands.add(ChatColor.stripColor(lore).replace(CC.translate(" - "), ""));
+ }
+ }
+
+ return commands;
+ }
+
+ public boolean isAirDrop(ItemStack item) {
+ if (item == null) return false;
+ if (!item.hasItemMeta()) return false;
+ if (!item.getItemMeta().hasLore()) return false;
+ if (item.getItemMeta().getDisplayName() == null) return false;
+ return item.getItemMeta().getDisplayName().equalsIgnoreCase(CC.translate("&6&lAirdrop"));
+ }
+}
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/java/rip/battle/crates/utils/RandomUtils.java b/Battle-Crates-main/src/main/java/rip/battle/crates/utils/RandomUtils.java
new file mode 100644
index 0000000..9455c38
--- /dev/null
+++ b/Battle-Crates-main/src/main/java/rip/battle/crates/utils/RandomUtils.java
@@ -0,0 +1,45 @@
+package rip.battle.crates.utils;
+
+import lombok.Getter;
+import lombok.experimental.UtilityClass;
+import rip.battle.crates.reward.Reward;
+
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
+@UtilityClass
+public class RandomUtils {
+
+ @Getter
+ private static final Random random = ThreadLocalRandom.current();
+
+ public double getDouble(double min, double max) {
+ return min + (max - min) * random.nextDouble();
+ }
+
+ public Reward getRandomReward(List list) {
+
+ list.removeIf(reward -> reward.getChance() <= 0D);
+
+ if (list.isEmpty()) return null;
+
+ Reward reward = null;
+
+ double total = list.stream().mapToDouble(Reward::getChance).sum();
+
+ double index = getDouble(0D, total);
+ double countWeight = 0D;
+
+ for (Reward reward1 : list) {
+ countWeight += reward1.getChance();
+ if (countWeight >= index) {
+ reward = reward1;
+ break;
+ }
+ }
+
+ return reward;
+ }
+
+}
diff --git a/Battle-Crates-main/src/main/resources/config.yml b/Battle-Crates-main/src/main/resources/config.yml
new file mode 100644
index 0000000..32dce3a
--- /dev/null
+++ b/Battle-Crates-main/src/main/resources/config.yml
@@ -0,0 +1,8 @@
+mongo:
+ host: localhost
+ port: 27017
+ database: "battlerip"
+ authentication:
+ enabled: false
+ username: "admin"
+ password: "admin"
\ No newline at end of file
diff --git a/Battle-Crates-main/src/main/resources/plugin.yml b/Battle-Crates-main/src/main/resources/plugin.yml
new file mode 100644
index 0000000..e763fef
--- /dev/null
+++ b/Battle-Crates-main/src/main/resources/plugin.yml
@@ -0,0 +1,6 @@
+main: rip.battle.crates.Crates
+name: Crates
+version: 1.0.0
+author: EnzoL_
+depend:
+ - Core
\ No newline at end of file
diff --git a/BattleEntity-main/.gitignore b/BattleEntity-main/.gitignore
new file mode 100644
index 0000000..4a410a1
--- /dev/null
+++ b/BattleEntity-main/.gitignore
@@ -0,0 +1,70 @@
+# Eclipse
+/.classpath
+/.project
+/.settings
+
+# Netbeans
+/nbproject
+
+# Fuck mauri!
+/build.xml
+
+# Maven
+/target
+/dependency-reduced-pom.xml
+*/target
+*/dependency-reduced-pom.xml
+
+# Vim
+.*.sw[a-p]
+
+# Various other potential build files
+/build
+/bin
+/dist
+/manifest.mf
+/MANIFEST.MF
+/META-INF/MANIFEST.MF
+git.properties
+.ssh
+key
+commiter.sh
+key.pub
+dependency-reduced-pom.xml
+
+# Mac Filesystem Dust
+.DS_Store
+
+# IntelliJ IDEA
+*.iml
+*.ipr
+*.iws
+.idea/
+
+#Libraries jar files
+libraries/*.jar
+=======
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+out
\ No newline at end of file
diff --git a/BattleEntity-main/pom.xml b/BattleEntity-main/pom.xml
new file mode 100644
index 0000000..95a96e9
--- /dev/null
+++ b/BattleEntity-main/pom.xml
@@ -0,0 +1,66 @@
+
+
+ 4.0.0
+
+ rip.battle.entity
+ BattleEntity
+ 1.0-SNAPSHOT
+
+
+ 8
+ 8
+ UTF-8
+
+
+
+ BattleEntity
+
+
+
+
+ viaversion-repo
+ https://repo.viaversion.com
+
+
+
+
+
+ rip.battle.spigot
+ battle-server
+ 1.8.8-R0.1-SNAPSHOT
+ provided
+
+
+
+ BattleAPI
+ cc.stormworth
+ 1.0-SNAPSHOT
+ provided
+
+
+
+ BattleBukkit
+ cc.stormworth
+ 1.0-SNAPSHOT
+ provided
+
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.24
+ provided
+
+
+
+ com.viaversion
+ viaversion-api
+ LATEST
+ provided
+
+
+
+
\ No newline at end of file
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/Entity.java b/BattleEntity-main/src/main/java/rip/battle/entity/Entity.java
new file mode 100644
index 0000000..062b75c
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/Entity.java
@@ -0,0 +1,272 @@
+package rip.battle.entity;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import lombok.Getter;
+import lombok.Setter;
+import net.minecraft.server.v1_8_R3.PacketPlayOutAnimation;
+import net.minecraft.server.v1_8_R3.PacketPlayOutEntityDestroy;
+import net.minecraft.server.v1_8_R3.PacketPlayOutEntityStatus;
+import net.minecraft.server.v1_8_R3.PacketPlayOutEntityTeleport;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import rip.battle.entity.hologram.api.Hologram;
+import rip.battle.entity.utils.AngleUtils;
+import rip.battle.entity.utils.PacketUtils;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+@Getter @Setter
+public abstract class Entity {
+
+ private static int ENTITY_ID = -1337;
+ private final int id;
+ private final UUID uuid;
+ private Location location;
+
+ private boolean initialized = false;
+ private boolean hidden = false;
+
+ private boolean persistent = true;
+
+ private boolean root = true;
+
+ private Set currentWatchers = Sets.newHashSet();
+ private Set hiddeFor = Sets.newHashSet();
+
+ private Set commands = Sets.newHashSet();
+
+ private Consumer onLeftClick;
+ private Consumer onRightClick;
+
+ public Entity(Location location) {
+ this.id = nextId();
+ this.location = location;
+ this.uuid = UUID.randomUUID();
+
+ EntityManager.addEntity(this);
+ }
+
+ public abstract String getTypeName();
+
+ public void initializeData() {
+ this.initialized = true;
+ }
+
+ public void onDelete(){
+ destroyForCurrentWatchers();
+
+ EntityManager.removeEntity(this);
+ }
+
+ public void updateLocation(Location location) {
+ this.location = location;
+
+ getCurrentWatchersPlayers().forEach(this::sendRePositionPackets);
+ }
+
+ public Hologram getAttachedHologram(){ //Add HologramEntity
+ return null;
+ }
+
+ public boolean isVisible() {
+ return !hidden;
+ }
+
+ public boolean isVisibleTo(Player player) {
+ return !hidden && location.getWorld() == player.getWorld() && location.distance(player.getLocation()) <= 32.0 && !hiddeFor.contains(player.getUniqueId());
+ }
+
+ public void updateVisibility(boolean hidden){
+ boolean hasChanged = this.hidden != hidden;
+
+ this.hidden = hidden;
+
+ if(hasChanged){
+ if(hidden){
+ destroyForCurrentWatchers();
+ }else{
+
+ Bukkit.getOnlinePlayers().forEach(player -> {
+ if (!currentWatchers.contains(player.getUniqueId()) && isVisibleTo(player)) {
+ currentWatchers.add(player.getUniqueId());
+ }
+ });
+
+ spawnForCurrentWatchers();
+ }
+ }
+
+ if (getAttachedHologram() != null) {
+ getAttachedHologram().updateVisibility(hidden);
+ }
+ }
+
+ public void updateVisibilityFor(Player player, boolean hidden) {
+ boolean hasChanged = currentWatchers.contains(player.getUniqueId()) != hidden;
+
+ if(hasChanged){
+
+ if(hidden){
+ hiddeFor.add(player.getUniqueId());
+ currentWatchers.remove(player.getUniqueId());
+ }else{
+ currentWatchers.add(player.getUniqueId());
+ }
+
+ if(hidden) {
+ sendDestroyPackets(player);
+ }else {
+ sendSpawnPackets(player);
+ }
+
+ if (getAttachedHologram() != null) {
+ getAttachedHologram().updateVisibilityFor(player, hidden);
+ }
+ }
+ }
+
+ public void spawnForCurrentWatchers() {
+ getCurrentWatchersPlayers().stream().filter(this::isVisibleTo).forEach(this::sendSpawnPackets);
+ }
+
+ public void onLeftClick(Player player) {
+ if (!commands.isEmpty()) {
+ for (String command : commands) {
+ player.performCommand(command.replace("{player}", player.getName()));
+ }
+ }else if (onLeftClick != null) {
+ onLeftClick.accept(player);
+ }
+ }
+
+ public void onRightClick(Player player) {
+ if (onRightClick != null) {
+ onRightClick.accept(player);
+ }
+ }
+
+ public void spawn(){
+ Bukkit.getOnlinePlayers().forEach(player -> {
+ if(isVisibleTo(player)){
+ spawn(player);
+ }
+ });
+ }
+
+ public boolean isDamageable() {
+ return false;
+ }
+
+ public void spawn(Player player){
+
+ /*if (currentWatchers.contains(player.getUniqueId())) {
+ return;
+ }*/
+
+ currentWatchers.add(player.getUniqueId());
+
+ sendSpawnPackets(player);
+ sendUpdatePackets(player);
+ }
+
+ public void update(Player player){
+
+ if (!currentWatchers.contains(player.getUniqueId())) {
+ return;
+ }
+
+ sendUpdatePackets(player);
+ }
+
+ public void destroy(Player player) {
+ currentWatchers.remove(player.getUniqueId());
+
+ sendDestroyPackets(player);
+ }
+
+ public boolean hasAnyWatchers() {
+ return currentWatchers.size() > 0;
+ }
+
+ public List getCurrentWatchersPlayers() {
+ return currentWatchers.stream().map(Bukkit::getPlayer).filter(Objects::nonNull).collect(Collectors.toList());
+ }
+
+ public boolean isWatcher(Player player){
+ return currentWatchers.contains(player.getUniqueId());
+ }
+
+ public void updateForCurrentWatchers() {
+ for (Player player : getCurrentWatchersPlayers()) {
+ update(player);
+ }
+ }
+
+ public void destroyForCurrentWatchers() {
+ for (Player player : getCurrentWatchersPlayers()) {
+ sendDestroyPackets(player);
+ }
+ }
+
+ public void resendForCurrentWatchers() {
+ for (Player player : getCurrentWatchersPlayers()) {
+ destroy(player);
+ spawn(player);
+ }
+ }
+
+ public boolean isMultiPartEntity(){
+ return false;
+ }
+
+ public boolean isRootOfMultiPartEntity(){
+ return root;
+ }
+
+ public void sendSpawnPackets(Player player) {}
+
+ public void sendDestroyPackets(Player player) {
+ PacketUtils.sendPacket(player, new PacketPlayOutEntityDestroy(id));
+ }
+
+ public void sendUpdatePackets(Player player) {}
+
+ public void sendRePositionPackets(Player player) {
+
+ PacketPlayOutEntityTeleport playOutEntityTeleport = new PacketPlayOutEntityTeleport(
+ this.id,
+ (int) (this.location.getX() * 32.0D),
+ (int) (this.location.getY() * 32.0D),
+ (int) (this.location.getZ() * 32.0D),
+ AngleUtils.yawToBytes(location.getYaw()),
+ AngleUtils.yawToBytes(location.getPitch()),
+ false
+ );
+
+ PacketUtils.sendPacket(player, playOutEntityTeleport);
+ }
+
+ public void sendStatusPacket(byte status){
+ PacketPlayOutEntityStatus packet = new PacketPlayOutEntityStatus(id, status);
+
+ PacketUtils.sendPacket(getCurrentWatchersPlayers(), packet);
+ }
+
+ public void sendAnimationPacket(int animation){
+ PacketPlayOutAnimation packet = new PacketPlayOutAnimation(id, animation);
+
+ PacketUtils.sendPacket(getCurrentWatchersPlayers(), packet);
+ }
+
+ public static int nextId() {
+ return ENTITY_ID--;
+ }
+
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/EntityManager.java b/BattleEntity-main/src/main/java/rip/battle/entity/EntityManager.java
new file mode 100644
index 0000000..05cc219
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/EntityManager.java
@@ -0,0 +1,24 @@
+package rip.battle.entity;
+
+import com.google.common.collect.Maps;
+import lombok.Getter;
+
+import java.util.Map;
+
+public class EntityManager {
+
+ @Getter private static final Map entities = Maps.newHashMap();
+
+ public static void addEntity(Entity entity) {
+ entities.put(entity.getId(), entity);
+ }
+
+ public static void removeEntity(Entity entity) {
+ entities.remove(entity.getId());
+ }
+
+ public static Entity getEntity(int id) {
+ return entities.get(id);
+ }
+
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/EntityPlugin.java b/BattleEntity-main/src/main/java/rip/battle/entity/EntityPlugin.java
new file mode 100644
index 0000000..d378d51
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/EntityPlugin.java
@@ -0,0 +1,114 @@
+package rip.battle.entity;
+
+import cc.stormworth.core.api.BattleAPI;
+import com.jonahseguin.drink.CommandService;
+import com.jonahseguin.drink.Drink;
+import com.jonahseguin.drink.command.DrinkCommandContainer;
+import lombok.Getter;
+import net.minecraft.server.v1_8_R3.PacketPlayInUseEntity;
+import org.bukkit.plugin.PluginManager;
+import org.bukkit.plugin.java.JavaPlugin;
+import rip.battle.entity.hologram.HologramManager;
+import rip.battle.entity.hologram.commands.HologramCommand;
+import rip.battle.entity.hologram.commands.sub.*;
+import rip.battle.entity.hologram.runnable.HologramUpdateRunnable;
+import rip.battle.entity.listeners.PlayerListener;
+import rip.battle.entity.npc.NPCManager;
+import rip.battle.entity.npc.commands.NPCCommand;
+import rip.battle.entity.npc.commands.sub.*;
+import rip.battle.entity.packet.PacketEntityUseAdapter;
+import rip.battle.entity.taks.RainbowTask;
+import rip.battle.entity.task.EntityVisibilityRunnable;
+import rip.battle.entity.tick.TickableEntity;
+
+@Getter
+public class EntityPlugin extends JavaPlugin {
+
+ private HologramManager hologramManager;
+ private NPCManager npcManager;
+ private BattleAPI battleAPI;
+
+ @Override
+ public void onEnable() {
+ registerManagers();
+ registerListeners();
+ registerCommands();
+ registerRunnables();
+ }
+
+ @Override
+ public void onDisable() {
+ hologramManager.saveHolograms();
+ npcManager.saveNPCs();
+ }
+
+ private void registerManagers(){
+ hologramManager = new HologramManager(this);
+ npcManager = new NPCManager(this);
+ battleAPI = BattleAPI.getBattleAPI(this);
+ }
+
+ private void registerRunnables(){
+ getServer().getScheduler().runTaskTimerAsynchronously(this, new HologramUpdateRunnable(hologramManager), 0L, 1L);
+ getServer().getScheduler().runTaskTimerAsynchronously(this, new RainbowTask(), 0L, 1L);
+ getServer().getScheduler().runTaskTimer(this, new EntityVisibilityRunnable(), 0L, 20L);
+ getServer().getScheduler().runTaskTimerAsynchronously(this, () -> {
+ for (Entity entity : EntityManager.getEntities().values()) {
+
+ if (entity instanceof TickableEntity){
+ TickableEntity livingEntity = (TickableEntity) entity;
+ livingEntity.tick();
+ }
+
+ }
+ }, 0L, 1L);
+ }
+
+ private void registerCommands(){
+ CommandService drink = Drink.get(this);
+
+ DrinkCommandContainer hologramContainer = drink.register(new HologramCommand(), "hologram");
+
+ hologramContainer.registerSub(new HologramCreateSubCommand(hologramManager));
+ hologramContainer.registerSub(new HologramDeleteSubCommand(hologramManager));
+ hologramContainer.registerSub(new HologramAddLineSubCommand(hologramManager));
+ hologramContainer.registerSub(new HologramAddItemLineSubCommand(hologramManager));
+ hologramContainer.registerSub(new HologramTeleportSubCommand(hologramManager));
+ hologramContainer.registerSub(new HologramMoveHereSubCommand(hologramManager));
+ hologramContainer.registerSub(new HologramSetLineSubCommand(hologramManager));
+ hologramContainer.registerSub(new HologramSetItemLineSubCommand(hologramManager));
+ hologramContainer.registerSub(new HologramListSubCommand(hologramManager));
+ hologramContainer.registerSub(new HologramRemoveLineSubCommand(hologramManager));
+ hologramContainer.registerSub(new HologramEditSubCommand(this, hologramManager));
+
+
+ DrinkCommandContainer npcContainer = drink.register(new NPCCommand(), "npc");
+ npcContainer.registerSub(new NPCCreateSubCommand(npcManager));
+ npcContainer.registerSub(new NPCDeleteSubCommand(npcManager));
+ npcContainer.registerSub(new NPCSetDisplaynameSubCommand(npcManager));
+ npcContainer.registerSub(new NPCSetSkinSubCommand(npcManager));
+ npcContainer.registerSub(new NPCSetArmorSubCommand(npcManager));
+ npcContainer.registerSub(new NPCAddItemLineSubCommand(npcManager));
+ npcContainer.registerSub(new NPCAddLineSubCommand(npcManager));
+ npcContainer.registerSub(new NPCMoveHereSubCommand(npcManager));
+ npcContainer.registerSub(new NPCTeleportSubCommand(npcManager));
+ npcContainer.registerSub(new NPCRemoveLineSubCommand(npcManager));
+ npcContainer.registerSub(new NPCSetLineSubCommand(npcManager));
+ npcContainer.registerSub(new NPCSetItemInHandSubCommand(npcManager));
+ npcContainer.registerSub(new NPCSetHelmetSubCommand(npcManager));
+ npcContainer.registerSub(new NPCSetChestPlateSubCommand(npcManager));
+ npcContainer.registerSub(new NPCSetLeggingsSubCommand(npcManager));
+ npcContainer.registerSub(new NPCSetBootsSubCommand(npcManager));
+ npcContainer.registerSub(new NPCListCommand(npcManager));
+ npcContainer.registerSub(new NPCEditSubCommand(this, npcManager));
+
+ drink.registerCommands();
+ }
+
+ private void registerListeners(){
+ battleAPI.registerAdapter(PacketPlayInUseEntity.class, new PacketEntityUseAdapter(this));
+
+ PluginManager pluginManager = getServer().getPluginManager();
+ pluginManager.registerEvents(new PlayerListener(), this);
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/CraftHologram.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/CraftHologram.java
new file mode 100644
index 0000000..6543896
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/CraftHologram.java
@@ -0,0 +1,308 @@
+package rip.battle.entity.hologram;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import lombok.Getter;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import rip.battle.entity.Entity;
+import rip.battle.entity.hologram.api.Hologram;
+import rip.battle.entity.hologram.lines.HologramLine;
+import rip.battle.entity.hologram.lines.impl.ItemLine;
+import rip.battle.entity.hologram.lines.impl.MultipleTextLine;
+import rip.battle.entity.hologram.lines.impl.SupplierMultipleTextLine;
+import rip.battle.entity.hologram.lines.impl.TextLine;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+@Getter
+public class CraftHologram implements Hologram {
+
+ private final List> lines = Lists.newArrayList();
+ private Location location;
+
+ private final Set currentWatchers = Sets.newHashSet();
+ private final Set hiddenPlayers = Sets.newHashSet();
+
+ private boolean hidden;
+
+ private Entity parent;
+
+ private String name;
+
+ public CraftHologram(String name, String line, Location location) {
+ this.name = name;
+ this.location = location;
+ addLine(line);
+ }
+
+ public CraftHologram(String name, ItemStack line, Location location) {
+ this.name = name;
+ this.location = location;
+ addLine(line);
+ }
+
+ public CraftHologram(String name, Location location) {
+ this.location = location;
+ this.name = name;
+ }
+
+ public boolean isVisible() {
+ return !hidden;
+ }
+
+ public boolean isVisibleTo(Player player) {
+ return !hidden && location.getWorld() == player.getWorld() && location.distance(player.getLocation()) <= 32.0 && !hiddenPlayers.contains(player.getUniqueId());
+ }
+
+ @Override
+ public void destroy() {
+ onDelete();
+ }
+
+ @Override
+ public void spawn() {
+ Bukkit.getOnlinePlayers().forEach(player -> {
+ if (isVisibleTo(player)) {
+ lines.forEach(line -> line.spawn(player));
+ currentWatchers.add(player.getUniqueId());
+ }
+ });
+ }
+
+ public void spawnFor(Player player) {
+ lines.forEach(line -> line.spawn(player));
+
+ currentWatchers.add(player.getUniqueId());
+ }
+
+ public void updateVisibility(boolean hidden){
+ boolean hasChanged = this.hidden != hidden;
+
+ this.hidden = hidden;
+
+ if(hasChanged){
+ lines.forEach(line -> line.updateVisibility(hidden));
+ }
+ }
+
+ @Override
+ public void updateVisibilityFor(Player player, boolean hidden) {
+ boolean hasChanged = currentWatchers.contains(player.getUniqueId()) != hidden;
+
+ if(hasChanged){
+
+ if(hidden){
+ hiddenPlayers.add(player.getUniqueId());
+ currentWatchers.remove(player.getUniqueId());
+ }else{
+ currentWatchers.add(player.getUniqueId());
+ }
+
+ lines.forEach(line -> {
+ if (line.isVisibleTo(player)) {
+ line.sendSpawnPackets(player);
+ }else{
+ line.sendDestroyPackets(player);
+ }
+ });
+ }
+ }
+
+ public List getCurrentWatchersPlayers() {
+ return currentWatchers.stream().map(Bukkit::getPlayer).filter(Objects::nonNull).collect(Collectors.toList());
+ }
+
+ public boolean isWatcher(Player player){
+ return currentWatchers.contains(player.getUniqueId());
+ }
+
+ protected void updateForCurrentWatchers() {
+ lines.forEach(Entity::updateForCurrentWatchers);
+ }
+
+
+ public void onDelete() {
+ lines.forEach(HologramLine::onDelete);
+ lines.clear();
+
+ currentWatchers.clear();
+ }
+
+ public void updateLocation(Location location){
+ this.location = location;
+
+ synchronizeLocations();
+ }
+
+ public void addLine(HologramLine> line) {
+ getCurrentWatchersPlayers().forEach(line::spawn);
+ lines.add(line);
+
+ synchronizeLocations();
+ }
+
+ @Override
+ public void addLine(String text){
+ addLine(player -> text);
+ }
+
+ @Override
+ public void addLine(Function line) {
+ addLine(line, null);
+ }
+
+ @Override
+ public void addLines(List> lines) {
+ lines.forEach(this::addLine);
+ }
+
+
+ @Override
+ public void addLines(Function> lines) {
+ addLine(new MultipleTextLine(this, this.location.clone(), lines));
+ }
+
+ @Override
+ public void addSupplierLines(Function>> lines) {
+ addLine(new SupplierMultipleTextLine(this, this.location.clone(), lines));
+ }
+
+ @Override
+ public void addLine(Function line, Consumer onClick) {
+ addLine(line, onClick, player -> true);
+ }
+
+ @Override
+ public void addLine(Function line, Consumer onClick, Predicate shouldSend) {
+ addLine(new TextLine(this, this.location.clone().subtract(0, 0.25, 0), line, onClick, shouldSend));
+ }
+
+ @Override
+ public void addLine(ItemStack itemStack){
+ addLine(itemStack, null);
+ }
+
+ @Override
+ public void addLine(ItemStack line, Consumer onClick) {
+ addLine(line, onClick, player -> true);
+ }
+
+ @Override
+ public void addLine(ItemStack line, Consumer onClick, Predicate shouldSend) {
+ addLine(new ItemLine(this, this.location.clone().subtract(0, 0.25, 0), line, onClick, shouldSend));
+ }
+
+ @Override
+ public void setLine(int line, String text) {
+ setLine(line, player -> text);
+ }
+
+ @Override
+ public void setLine(int line, Function text) {
+ setLine(line, text, null);
+ }
+
+ @Override
+ public void setLine(int line, Function text, Consumer onClick) {
+ setLine(line, text, onClick, player -> true);
+ }
+
+ @Override
+ public void setLine(int line, Function text, Consumer onClick, Predicate shouldSend) {
+ HologramLine> hologramLine = lines.get(line);
+
+ if(hologramLine instanceof TextLine){
+ TextLine textLine = (TextLine) hologramLine;
+ textLine.setText(text);
+ textLine.setOnLeftClick(onClick);
+ textLine.setShouldSend(shouldSend);
+
+ textLine.updateForCurrentWatchers();
+ }
+ }
+
+ @Override
+ public void setLine(int line, ItemStack itemStack) {
+ setLine(line, itemStack, null);
+ }
+
+ @Override
+ public void setLine(int line, ItemStack itemStack, Consumer onClick) {
+ setLine(line, itemStack, onClick, player -> true);
+ }
+
+ @Override
+ public void setLine(int line, ItemStack itemStack, Consumer onClick, Predicate shouldSend) {
+ HologramLine> hologramLine = lines.get(line);
+
+ if(hologramLine instanceof ItemLine){
+ ItemLine itemLine = (ItemLine) hologramLine;
+ itemLine.setItemStack(itemStack);
+ itemLine.setOnLeftClick(onClick);
+ itemLine.setShouldSend(shouldSend);
+
+ itemLine.updateForCurrentWatchers();
+ }
+ }
+
+ @Override
+ public void removeLine(HologramLine> line) {
+ lines.remove(line);
+
+ getCurrentWatchersPlayers().forEach(line::destroy);
+ }
+
+ @Override
+ public void removeLine(int index) {
+ removeLine(lines.remove(index));
+ }
+
+ @Override
+ public void teleport(Location location) {
+ updateLocation(location);
+ }
+
+ public void clearLines() {
+ getCurrentWatchersPlayers().forEach(player -> {
+ for (HologramLine> line : lines) {
+ line.sendDestroyPackets(player);
+ }
+ });
+ lines.clear();
+ }
+
+ protected void synchronizeLocations(){
+ Location startLocation = location.clone().add(0, lines.size() * 0.25, 0);
+
+ for (HologramLine> line : lines) {
+ if (line instanceof ItemLine){
+ startLocation.subtract(0, 0.25, 0);
+ line.updateLocation(startLocation.clone().add(0.0, 0.5, 0.0));
+ startLocation.subtract(0, 0.5, 0);
+ }if (line instanceof TextLine) {
+ line.updateLocation(startLocation.clone());
+ startLocation.subtract(0, 0.25, 0);
+ }else if (line instanceof MultipleTextLine){
+ MultipleTextLine multipleTextLine = (MultipleTextLine) line;
+ multipleTextLine.updateLocation(startLocation.clone().add(0, 0.25, 0));
+ startLocation.subtract(0, 0.25 * multipleTextLine.getLines().apply(Bukkit.getConsoleSender()).size(), 0);
+ }
+ }
+ }
+
+ public void setParent(Entity parent) {
+ this.parent = parent;
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/HologramBuilder.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/HologramBuilder.java
new file mode 100644
index 0000000..a7952dc
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/HologramBuilder.java
@@ -0,0 +1,133 @@
+package rip.battle.entity.hologram;
+
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import rip.battle.entity.hologram.api.Hologram;
+import rip.battle.entity.hologram.impl.UpdatableHologram;
+import rip.battle.entity.hologram.lines.HologramLine;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public class HologramBuilder {
+
+ private final Hologram hologram;
+
+ public HologramBuilder(String name, Location location, boolean updatable) {
+ if (updatable) {
+ hologram = new UpdatableHologram(name, location) {
+ @Override
+ public int getUpdateInterval() {
+ return 1;
+ }
+ };
+ } else {
+ hologram = new CraftHologram(name, location);
+ }
+ }
+
+ public HologramBuilder addLine(String line) {
+ hologram.addLine(line);
+ return this;
+ }
+
+ public HologramBuilder addLine(ItemStack line) {
+ hologram.addLine(line);
+ return this;
+ }
+
+ public HologramBuilder addLine(ItemStack line, Consumer onClick) {
+ hologram.addLine(line, onClick);
+ return this;
+ }
+
+ public HologramBuilder addLine(ItemStack line, Consumer onClick, Predicate shouldSend) {
+ hologram.addLine(line, onClick, shouldSend);
+ return this;
+ }
+
+ public HologramBuilder addLine(String line, Consumer onClick) {
+ hologram.addLine(player -> line, onClick);
+ return this;
+ }
+
+ public HologramBuilder addLine(String line, Consumer onClick, Predicate shouldSend) {
+ hologram.addLine(player -> line, onClick, shouldSend);
+ return this;
+ }
+
+
+ public HologramBuilder addLine(Function line, Consumer onClick, Predicate shouldSend) {
+ hologram.addLine(line, onClick, shouldSend);
+ return this;
+ }
+
+ public HologramBuilder addLine(Function line, Consumer onClick) {
+ hologram.addLine(line, onClick);
+ return this;
+ }
+
+ public HologramBuilder addLine(Function line) {
+ hologram.addLine(line);
+ return this;
+ }
+
+ public HologramBuilder removeLine(int index) {
+ hologram.removeLine(index);
+ return this;
+ }
+
+ public HologramBuilder removeLine(HologramLine> line) {
+ hologram.removeLine(line);
+ return this;
+ }
+
+ public HologramBuilder addLines(String... lines) {
+ for (String line : lines) {
+ hologram.addLine(line);
+ }
+ return this;
+ }
+
+ public HologramBuilder addLines(ItemStack... lines) {
+ for (ItemStack line : lines) {
+ hologram.addLine(line);
+ }
+ return this;
+ }
+
+ @SafeVarargs
+ public final HologramBuilder addLines(Function... lines) {
+ for (Function line : lines) {
+ hologram.addLine(line);
+ }
+ return this;
+ }
+
+ public HologramBuilder hide() {
+ hologram.updateVisibility(true);
+ return this;
+ }
+
+ public HologramBuilder show() {
+ hologram.updateVisibility(false);
+ return this;
+ }
+
+ public HologramBuilder hideFor(Player player) {
+ hologram.updateVisibilityFor(player, true);
+ return this;
+ }
+
+ public HologramBuilder showFor(Player player) {
+ hologram.updateVisibilityFor(player, false);
+ return this;
+ }
+
+ public Hologram build() {
+ return hologram;
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/HologramManager.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/HologramManager.java
new file mode 100644
index 0000000..560059c
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/HologramManager.java
@@ -0,0 +1,122 @@
+package rip.battle.entity.hologram;
+
+import cc.stormworth.core.util.serialize.ItemStackAdapter;
+import com.google.common.collect.Maps;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import lombok.Getter;
+import org.bukkit.Location;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.plugin.java.JavaPlugin;
+import rip.battle.entity.EntityPlugin;
+import rip.battle.entity.hologram.api.Hologram;
+import rip.battle.entity.hologram.impl.UpdatableHologram;
+import rip.battle.entity.hologram.serialization.HologramTypeAdapter;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Map;
+
+public class HologramManager {
+
+ @Getter private final Map holograms = Maps.newHashMap();
+
+ private final EntityPlugin plugin;
+
+ public HologramManager(EntityPlugin plugin){
+ this.plugin = plugin;
+
+ loadHolograms();
+ }
+
+ public void loadHolograms(){
+ File file = new File(plugin.getDataFolder(), "holograms");
+
+ if (!file.exists()){
+ return;
+ }
+
+ Arrays.stream(file.listFiles()).forEach(file1 -> {
+
+ FileReader fileReader;
+
+ try {
+ fileReader = new FileReader(file1);
+
+ JsonElement jsonElement = new Gson().fromJson(fileReader, JsonElement.class);
+
+ System.out.println(jsonElement.toString());
+ Hologram hologram = HologramTypeAdapter.fromJson(jsonElement);
+
+ holograms.put(file1.getName().replace(".json", ""), hologram);
+
+ System.out.println("Loaded hologram " + file1.getName());
+ System.out.println("hologram " + ((CraftHologram)hologram).getLocation());
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ });
+ }
+
+ public void saveHolograms(){
+
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(ItemStack.class, new ItemStackAdapter())
+ .registerTypeAdapter(Hologram.class, new HologramTypeAdapter())
+ .enableComplexMapKeySerialization()
+ .setPrettyPrinting()
+ .create();
+
+ File file = new File(plugin.getDataFolder(), "holograms");
+
+ if (!file.exists())
+ file.mkdirs();
+
+ holograms.forEach((name, hologram) -> {
+ try {
+ FileWriter writer = new FileWriter(new File(plugin.getDataFolder(),
+ File.separator + "holograms" + File.separator + name + ".json"));
+
+ writer.write(gson.toJson(HologramTypeAdapter.toJson(hologram)));
+ System.out.println("Saved hologram " + name);
+
+ writer.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ });
+ }
+
+ public void addHologram(String name, Hologram hologram) {
+ this.holograms.put(name, hologram);
+ }
+
+ public void removeHologram(String name) {
+ Hologram hologram = this.holograms.remove(name);
+
+ if (hologram != null) {
+ hologram.destroy();
+ }
+
+ File file = new File(plugin.getDataFolder(), "holograms" + File.separator + name + ".json");
+
+ if (file.exists())
+ file.delete();
+ }
+
+ public Hologram getHologram(String name) {
+ return this.holograms.get(name);
+ }
+
+ public void createHologram(String name, Location location) {
+ Hologram hologram = new UpdatableHologram(name, location);
+
+ hologram.spawn();
+ addHologram(name, hologram);
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/api/Hologram.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/api/Hologram.java
new file mode 100644
index 0000000..d871c31
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/api/Hologram.java
@@ -0,0 +1,74 @@
+package rip.battle.entity.hologram.api;
+
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import rip.battle.entity.hologram.lines.HologramLine;
+
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+public interface Hologram {
+
+ String getName();
+
+ List> getLines();
+
+ void addLine(String line);
+
+ void addLine(Function line);
+
+ void addLines(List> lines);
+
+ void addLines(Function> lines);
+ void addSupplierLines(Function>> lines);
+
+ void addLine(Function line, Consumer onClick);
+
+ void addLine(Function line, Consumer onClick, Predicate shouldSend);
+
+ void addLine(ItemStack line);
+
+ void addLine(ItemStack line, Consumer onClick);
+
+ void addLine(ItemStack line, Consumer onClick, Predicate shouldSend);
+
+ void setLine(int line, String text);
+
+ void setLine(int line, Function text);
+
+ void setLine(int line, Function text, Consumer onClick);
+
+ void setLine(int line, Function text, Consumer onClick, Predicate shouldSend);
+
+ void setLine(int line, ItemStack itemStack);
+
+ void setLine(int line, ItemStack itemStack, Consumer onClick);
+
+ void setLine(int line, ItemStack itemStack, Consumer onClick, Predicate shouldSend);
+
+ void removeLine(HologramLine> line);
+
+ void removeLine(int index);
+
+ void teleport(Location location);
+
+ Location getLocation();
+
+ boolean isVisible();
+
+ void updateVisibility(boolean hidden);
+
+ void updateVisibilityFor(Player player, boolean hidden);
+
+ boolean isVisibleTo(Player player);
+
+ void destroy();
+
+ void spawn();
+
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/HologramCommand.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/HologramCommand.java
new file mode 100644
index 0000000..689b221
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/HologramCommand.java
@@ -0,0 +1,30 @@
+package rip.battle.entity.hologram.commands;
+
+import cc.stormworth.core.util.chat.CC;
+import com.jonahseguin.drink.annotation.Command;
+import com.jonahseguin.drink.annotation.Require;
+import com.jonahseguin.drink.annotation.Sender;
+import org.bukkit.entity.Player;
+
+public class HologramCommand {
+
+ @Command(name = "", desc = "Allows you to manage your holograms", usage = "")
+ @Require("battle.entity.hologram")
+ public void hologram(@Sender Player player) {
+ player.sendMessage(CC.translate("&e&lHologram Commands"));
+
+ player.sendMessage(CC.translate("&e/hologram create &7- &fAllows you to create a hologram."));
+ player.sendMessage(CC.translate("&e/hologram delete &7- &fAllows you to delete a hologram."));
+ player.sendMessage(CC.translate("&e/hologram addline &7- &fAllows you to add a line to a hologram."));
+ player.sendMessage(CC.translate("&e/hologram setline &7- &fAllows you to set a line on a hologram."));
+ player.sendMessage(CC.translate("&e/hologram additemline &7- &fAllows you to add an item line to a hologram."));
+ player.sendMessage(CC.translate("&e/hologram setitemline - &7- &fAllows you to set an item line on a hologram."));
+ player.sendMessage(CC.translate("&e/hologram removeline &7- &fAllows you to remove a line from a hologram."));
+ player.sendMessage(CC.translate("&e/hologram teleport &7- &fAllows you to teleport to a hologram."));
+ player.sendMessage(CC.translate("&e/hologram movehere &7- &fAllows you to move a hologram to your location."));
+ player.sendMessage(CC.translate("&e/hologram list &7- &fAllows you to list all holograms."));
+ player.sendMessage(CC.translate("&e/hologram edit &7- &fAllows you to edit a hologram."));
+
+ }
+
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramAddItemLineSubCommand.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramAddItemLineSubCommand.java
new file mode 100644
index 0000000..9a06178
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramAddItemLineSubCommand.java
@@ -0,0 +1,35 @@
+package rip.battle.entity.hologram.commands.sub;
+
+import cc.stormworth.core.util.chat.CC;
+import com.jonahseguin.drink.annotation.Command;
+import com.jonahseguin.drink.annotation.Sender;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.entity.Player;
+import rip.battle.entity.hologram.HologramManager;
+import rip.battle.entity.hologram.api.Hologram;
+
+@RequiredArgsConstructor
+public class HologramAddItemLineSubCommand {
+
+ private final HologramManager hologramManager;
+
+ @Command(name = "additemline", desc = "Allows you to add an item line to a hologram", usage = "
- ")
+ public void addline(@Sender Player player, String name) {
+
+ Hologram hologram = hologramManager.getHologram(name);
+
+ if (hologram == null) {
+ player.sendMessage(CC.translate("&cA hologram with that name does not exist."));
+ return;
+ }
+
+ if (player.getItemInHand() == null) {
+ player.sendMessage(CC.translate("&cYou must be holding an item in your hand."));
+ return;
+ }
+
+ hologram.addLine(player.getItemInHand());
+ player.sendMessage(CC.translate("&aYou have added an item line to the hologram &e" + name + "&a."));
+ }
+
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramAddLineSubCommand.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramAddLineSubCommand.java
new file mode 100644
index 0000000..83af3bc
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramAddLineSubCommand.java
@@ -0,0 +1,31 @@
+package rip.battle.entity.hologram.commands.sub;
+
+import cc.stormworth.core.util.chat.CC;
+import com.jonahseguin.drink.annotation.Command;
+import com.jonahseguin.drink.annotation.Sender;
+import com.jonahseguin.drink.annotation.Text;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.entity.Player;
+import rip.battle.entity.hologram.HologramManager;
+import rip.battle.entity.hologram.api.Hologram;
+
+@RequiredArgsConstructor
+public class HologramAddLineSubCommand {
+
+ private final HologramManager hologramManager;
+
+ @Command(name = "addline", desc = "Allows you to add a line to a hologram", usage = " ")
+ public void addline(@Sender Player player, String name, @Text String line) {
+
+ Hologram hologram = hologramManager.getHologram(name);
+
+ if (hologram == null) {
+ player.sendMessage(CC.translate("&cA hologram with that name does not exist."));
+ return;
+ }
+
+ hologram.addLine(line);
+ player.sendMessage(CC.translate("&aYou have added a line to the hologram &e" + name + "&a."));
+ }
+
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramCreateSubCommand.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramCreateSubCommand.java
new file mode 100644
index 0000000..1db4871
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramCreateSubCommand.java
@@ -0,0 +1,27 @@
+package rip.battle.entity.hologram.commands.sub;
+
+import cc.stormworth.core.util.chat.CC;
+import com.jonahseguin.drink.annotation.Command;
+import com.jonahseguin.drink.annotation.Sender;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.entity.Player;
+import rip.battle.entity.hologram.HologramManager;
+
+@RequiredArgsConstructor
+public class HologramCreateSubCommand {
+
+ private final HologramManager hologramManager;
+
+ @Command(name = "create", desc = "Allows you to create a hologram", usage = "")
+ public void create(@Sender Player player, String name) {
+
+ if (hologramManager.getHologram(name) != null) {
+ player.sendMessage(CC.translate("&cA hologram with that name already exists."));
+ return;
+ }
+
+ hologramManager.createHologram(name, player.getLocation());
+ player.sendMessage(CC.translate("&aYou have created a hologram with the name &e" + name + "&a."));
+ }
+
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramDeleteSubCommand.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramDeleteSubCommand.java
new file mode 100644
index 0000000..eb898be
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramDeleteSubCommand.java
@@ -0,0 +1,29 @@
+package rip.battle.entity.hologram.commands.sub;
+
+import cc.stormworth.core.util.chat.CC;
+import com.jonahseguin.drink.annotation.Command;
+import com.jonahseguin.drink.annotation.Sender;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.entity.Player;
+import rip.battle.entity.hologram.HologramManager;
+import rip.battle.entity.hologram.api.Hologram;
+
+@RequiredArgsConstructor
+public class HologramDeleteSubCommand {
+
+ private final HologramManager hologramManager;
+
+ @Command(name = "delete", desc = "Allows you to delete a hologram", usage = "")
+ public void delete(@Sender Player player, String name) {
+ Hologram hologram = hologramManager.getHologram(name);
+
+ if (hologram == null) {
+ player.sendMessage(CC.translate("&cA hologram with that name does not exist."));
+ return;
+ }
+
+ hologramManager.removeHologram(name);
+ player.sendMessage(CC.translate("&aYou have deleted the hologram &e" + name + "&a."));
+ }
+
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramEditSubCommand.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramEditSubCommand.java
new file mode 100644
index 0000000..b858069
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramEditSubCommand.java
@@ -0,0 +1,32 @@
+package rip.battle.entity.hologram.commands.sub;
+
+import com.jonahseguin.drink.annotation.Command;
+import com.jonahseguin.drink.annotation.Sender;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.ChatColor;
+import org.bukkit.entity.Player;
+import rip.battle.entity.EntityPlugin;
+import rip.battle.entity.hologram.HologramManager;
+import rip.battle.entity.hologram.api.Hologram;
+import rip.battle.entity.hologram.menu.HologramEditMenu;
+
+@RequiredArgsConstructor
+public class HologramEditSubCommand {
+
+ private final EntityPlugin plugin;
+ private final HologramManager hologramManager;
+
+ @Command(name = "edit", desc = "Edit a hologram")
+ public void edit(@Sender Player player, String name){
+
+ Hologram hologram = hologramManager.getHologram(name);
+
+ if (hologram == null){
+ player.sendMessage(ChatColor.RED + "Hologram not found.");
+ return;
+ }
+
+ new HologramEditMenu(plugin, hologram, null).open(player);
+ }
+
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramListSubCommand.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramListSubCommand.java
new file mode 100644
index 0000000..1b86ae9
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramListSubCommand.java
@@ -0,0 +1,43 @@
+package rip.battle.entity.hologram.commands.sub;
+
+import cc.stormworth.core.util.chat.CC;
+import com.jonahseguin.drink.annotation.Command;
+import com.jonahseguin.drink.annotation.Sender;
+import lombok.RequiredArgsConstructor;
+import net.md_5.bungee.api.chat.ClickEvent;
+import net.md_5.bungee.api.chat.ComponentBuilder;
+import net.md_5.bungee.api.chat.HoverEvent;
+import org.bukkit.entity.Player;
+import rip.battle.entity.hologram.HologramManager;
+import rip.battle.entity.hologram.api.Hologram;
+
+import java.util.Map;
+
+@RequiredArgsConstructor
+public class HologramListSubCommand {
+
+ private final HologramManager hologramManager;
+
+ @Command(name = "list", desc = "Allows you to list all holograms", aliases = {"l", "ls"})
+ public void list(@Sender Player player) {
+
+ Map holograms = hologramManager.getHolograms();
+
+ if (holograms.isEmpty()) {
+ player.sendMessage(CC.translate("&cThere are no holograms."));
+ return;
+ }
+
+ player.sendMessage(CC.translate("&aThere are &e" + holograms.size() + " &aholograms."));
+
+ holograms.forEach((name, hologram) -> {
+
+ ComponentBuilder builder = new ComponentBuilder(CC.translate("&e" + name + " &b[Click to teleport]"));
+ builder.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(CC.translate("&7Click to teleport")).create()));
+ builder.event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/hologram tp " + name));
+
+ player.spigot().sendMessage(builder.create());
+ });
+ }
+
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramMoveHereSubCommand.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramMoveHereSubCommand.java
new file mode 100644
index 0000000..27db0b0
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramMoveHereSubCommand.java
@@ -0,0 +1,30 @@
+package rip.battle.entity.hologram.commands.sub;
+
+import cc.stormworth.core.util.chat.CC;
+import com.jonahseguin.drink.annotation.Command;
+import com.jonahseguin.drink.annotation.Sender;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.entity.Player;
+import rip.battle.entity.hologram.HologramManager;
+import rip.battle.entity.hologram.api.Hologram;
+
+@RequiredArgsConstructor
+public class HologramMoveHereSubCommand {
+
+ private final HologramManager hologramManager;
+
+ @Command(name = "movehere", desc = "Allows you to move a hologram to your location", usage = "", aliases = {"move", "moveto", "teleporto", "s"})
+ public void movehere(@Sender Player player, String name) {
+
+ Hologram hologram = hologramManager.getHologram(name);
+
+ if (hologram == null) {
+ player.sendMessage(CC.translate("&cA hologram with that name does not exist."));
+ return;
+ }
+
+ hologram.teleport(player.getLocation());
+ player.sendMessage(CC.translate("&aYou have moved the hologram &e" + name + "&a to your location."));
+ }
+
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramRemoveLineSubCommand.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramRemoveLineSubCommand.java
new file mode 100644
index 0000000..4a993eb
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramRemoveLineSubCommand.java
@@ -0,0 +1,32 @@
+package rip.battle.entity.hologram.commands.sub;
+
+import cc.stormworth.core.util.chat.CC;
+import com.jonahseguin.drink.annotation.Command;
+import com.jonahseguin.drink.annotation.Sender;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.entity.Player;
+import rip.battle.entity.hologram.HologramManager;
+import rip.battle.entity.hologram.api.Hologram;
+
+@RequiredArgsConstructor
+public class HologramRemoveLineSubCommand {
+
+ private final HologramManager hologramManager;
+
+ @Command(name = "removeline", desc = "Allows you to remove a line of a hologram", usage = " ")
+ public void removeLine(@Sender Player player, String name, int line) {
+
+ Hologram hologram = hologramManager.getHologram(name);
+
+ if (hologram == null) {
+ player.sendMessage(CC.translate("&cA hologram with that name does not exist."));
+ return;
+ }
+
+ hologram.removeLine(line);
+
+ player.sendMessage(CC.translate("&aYou have removed a line of the hologram &e" + name + "&a."));
+ }
+
+
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramSetItemLineSubCommand.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramSetItemLineSubCommand.java
new file mode 100644
index 0000000..4f22305
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramSetItemLineSubCommand.java
@@ -0,0 +1,38 @@
+package rip.battle.entity.hologram.commands.sub;
+
+import cc.stormworth.core.util.chat.CC;
+import com.jonahseguin.drink.annotation.Command;
+import com.jonahseguin.drink.annotation.Sender;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import rip.battle.entity.hologram.HologramManager;
+import rip.battle.entity.hologram.api.Hologram;
+
+@RequiredArgsConstructor
+public class HologramSetItemLineSubCommand {
+
+ private final HologramManager hologramManager;
+
+ @Command(name = "setitemline", desc = "Allows you to set a item line of a hologram", usage = " ")
+ public void setItemLine(@Sender Player player, String name, int line) {
+ Hologram hologram = hologramManager.getHologram(name);
+
+ if (hologram == null) {
+ player.sendMessage(CC.translate("&cA hologram with that name does not exist."));
+ return;
+ }
+
+ ItemStack item = player.getItemInHand();
+
+ if (item == null) {
+ player.sendMessage(CC.translate("&cYou must be holding an item in your hand."));
+ return;
+ }
+
+ hologram.setLine(line, item);
+
+ player.sendMessage(CC.translate("&aYou have set a item line of the hologram &e" + name + "&a."));
+ }
+
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramSetLineSubCommand.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramSetLineSubCommand.java
new file mode 100644
index 0000000..320c90f
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramSetLineSubCommand.java
@@ -0,0 +1,31 @@
+package rip.battle.entity.hologram.commands.sub;
+
+import cc.stormworth.core.util.chat.CC;
+import com.jonahseguin.drink.annotation.Command;
+import com.jonahseguin.drink.annotation.Sender;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.entity.Player;
+import rip.battle.entity.hologram.HologramManager;
+import rip.battle.entity.hologram.api.Hologram;
+
+@RequiredArgsConstructor
+public class HologramSetLineSubCommand {
+
+ private final HologramManager hologramManager;
+
+ @Command(name = "setline", desc = "Allows you to set a line of a hologram", usage = " ")
+ public void setline(@Sender Player player, String name, int line, String text) {
+
+ Hologram hologram = hologramManager.getHologram(name);
+
+ if (hologram == null) {
+ player.sendMessage(CC.translate("&cA hologram with that name does not exist."));
+ return;
+ }
+
+ hologram.setLine(line, text);
+
+ player.sendMessage(CC.translate("&aYou have set a line of the hologram &e" + name + "&a."));
+ }
+
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramTeleportSubCommand.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramTeleportSubCommand.java
new file mode 100644
index 0000000..6b157d3
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/commands/sub/HologramTeleportSubCommand.java
@@ -0,0 +1,30 @@
+package rip.battle.entity.hologram.commands.sub;
+
+import cc.stormworth.core.util.chat.CC;
+import com.jonahseguin.drink.annotation.Command;
+import com.jonahseguin.drink.annotation.Sender;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.entity.Player;
+import rip.battle.entity.hologram.HologramManager;
+import rip.battle.entity.hologram.api.Hologram;
+
+@RequiredArgsConstructor
+public class HologramTeleportSubCommand {
+
+ private final HologramManager hologramManager;
+
+ @Command(name = "teleport", desc = "Allows you to teleport to a hologram", usage = "", aliases = {"tp"})
+ public void teleport(@Sender Player player, String name) {
+
+ Hologram hologram = hologramManager.getHologram(name);
+
+ if (hologram == null) {
+ player.sendMessage(CC.translate("&cA hologram with that name does not exist."));
+ return;
+ }
+
+ player.teleport(hologram.getLocation());
+ player.sendMessage(CC.translate("&aYou have teleported to the hologram &e" + name + "&a."));
+ }
+
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/impl/UpdatableHologram.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/impl/UpdatableHologram.java
new file mode 100644
index 0000000..78f95b9
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/impl/UpdatableHologram.java
@@ -0,0 +1,40 @@
+package rip.battle.entity.hologram.impl;
+
+import lombok.Setter;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import rip.battle.entity.hologram.CraftHologram;
+import rip.battle.entity.hologram.lines.HologramLine;
+
+@Setter
+public class UpdatableHologram extends CraftHologram {
+
+ private long lastUpdate = System.currentTimeMillis();
+
+ public UpdatableHologram(String name, String line, Location location) {
+ super(name, line, location);
+ }
+
+ public UpdatableHologram(String name, ItemStack line, Location location) {
+ super(name, line, location);
+ }
+
+ public UpdatableHologram(String name, Location location) {
+ super(name, location);
+ }
+
+ public void update(Player player){
+ for (HologramLine> line : getLines()){
+ line.update(player);
+ }
+ }
+
+ public int getUpdateInterval(){
+ return 1;
+ }
+
+ public boolean canUpdate() {
+ return System.currentTimeMillis() - lastUpdate > getUpdateInterval();
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/lines/HologramLine.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/lines/HologramLine.java
new file mode 100644
index 0000000..a12eb9b
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/lines/HologramLine.java
@@ -0,0 +1,100 @@
+package rip.battle.entity.hologram.lines;
+
+import lombok.Getter;
+import lombok.Setter;
+import net.minecraft.server.v1_8_R3.PacketPlayOutAttachEntity;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import rip.battle.entity.Entity;
+import rip.battle.entity.hologram.CraftHologram;
+import rip.battle.entity.utils.PacketUtils;
+
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+@Getter
+@Setter
+public abstract class HologramLine extends Entity {
+
+ private final CraftHologram parent;
+ private Predicate shouldSend;
+
+ private final int supportEntityID = nextId();
+
+ public HologramLine(CraftHologram parent, Location location) {
+ this(parent, location, player -> true, null);
+ }
+
+ public HologramLine(CraftHologram parent, Location location, Predicate shouldSend) {
+ this(parent, location, shouldSend, null);
+ }
+
+ public HologramLine(CraftHologram parent, Location location, Consumer onClick) {
+ this(parent, location, player -> true, onClick);
+ }
+
+ public HologramLine(CraftHologram parent, Location location, Predicate shouldSend, Consumer onClick) {
+ super(location);
+ this.parent = parent;
+ this.shouldSend = shouldSend;
+ this.setOnLeftClick(onClick);
+ }
+
+ public boolean shouldSend(Player player) {
+ return shouldSend.test(player);
+ }
+
+ /*@Override
+ public void onLeftClick(Player player) {
+ player.sendMessage("You clicked a hologram line!");
+ }*/
+
+ @Override
+ public void spawn() {
+ super.spawn();
+
+ parent.getCurrentWatchers().addAll(getCurrentWatchers());
+ }
+
+ @Override
+ public void spawn(Player player) {
+ super.spawn(player);
+
+ parent.getCurrentWatchers().add(player.getUniqueId());
+ }
+
+ @Override
+ public void destroy(Player player) {
+ super.destroy(player);
+
+ parent.getCurrentWatchers().remove(player.getUniqueId());
+ }
+
+ public abstract T getLine(Player player);
+
+ public abstract void setLine(T line);
+
+ public void sendAttachPacket(Player player){
+ CraftHologram hologram = getParent();
+
+ if (hologram == null || hologram.getParent() == null)
+ return;
+
+ PacketUtils.sendPacket(player, new PacketPlayOutAttachEntity(0, supportEntityID, hologram.getParent().getId()));
+ }
+
+ @Override
+ public String getTypeName() {
+ return "Hologram_Line";
+ }
+
+ @Override
+ public boolean isRoot() {
+ return false;
+ }
+
+ @Override
+ public boolean isMultiPartEntity() {
+ return true;
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/lines/impl/ItemLine.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/lines/impl/ItemLine.java
new file mode 100644
index 0000000..c0bceda
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/lines/impl/ItemLine.java
@@ -0,0 +1,184 @@
+package rip.battle.entity.hologram.lines.impl;
+
+import lombok.Getter;
+import lombok.Setter;
+import net.minecraft.server.v1_8_R3.*;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;
+import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftItemStack;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import rip.battle.entity.hologram.CraftHologram;
+import rip.battle.entity.hologram.lines.HologramLine;
+import rip.battle.entity.utils.DataWatcherUtil;
+import rip.battle.entity.utils.PacketUtils;
+import rip.battle.entity.utils.PlayerUtil;
+
+import java.util.Collections;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+@Getter @Setter
+public class ItemLine extends HologramLine {
+
+ private ItemStack itemStack;
+
+ public ItemLine(CraftHologram parent, Location location, ItemStack itemStack) {
+ this(parent, location, itemStack, null);
+ }
+
+ public ItemLine(CraftHologram parent, Location location, ItemStack itemStack, Consumer onClick) {
+ this(parent, location, itemStack, onClick, player -> true);
+ }
+
+ public ItemLine(CraftHologram parent, Location location, ItemStack itemStack, Consumer onClick, Predicate shouldSend) {
+ super(parent, location, shouldSend, onClick);
+ this.itemStack = itemStack;
+ setPersistent(false);
+ }
+
+ @Override
+ public void sendSpawnPackets(Player player) {
+ boolean legacy = PlayerUtil.isLegacy(player);
+
+ PacketPlayOutSpawnEntity spawnPacket = new PacketPlayOutSpawnEntity(
+ getId(),
+ 2,
+ (int) getLocation().getX(),
+ (int) getLocation().getY(),
+ (int) getLocation().getZ(),
+ 0,
+ 0
+ );
+
+ DataWatcher itemDataWatcher = DataWatcherUtil.createDataWatcher();
+
+ itemDataWatcher.a(0, (byte) 0);
+ itemDataWatcher.a(1, (short) 0);
+ itemDataWatcher.a(2, "");
+ itemDataWatcher.a(3, (byte) 0);
+ itemDataWatcher.a(4, (byte) 0);
+
+ itemDataWatcher.add(10, 5);
+ itemDataWatcher.watch(10, CraftItemStack.asNMSCopy(itemStack));
+
+ DataWatcherUtil.setFlag(itemDataWatcher, 5, false);
+
+ PacketPlayOutEntityMetadata metadataPacket = new PacketPlayOutEntityMetadata(getId(), itemDataWatcher, true);
+
+ DataWatcher armorStandDataWatcher = DataWatcherUtil.createDataWatcher();
+
+ if (!legacy) {
+ armorStandDataWatcher.a(0, (byte) 0);
+ armorStandDataWatcher.a(1, (short) 0);
+ armorStandDataWatcher.a(2, "");
+ armorStandDataWatcher.a(3, (byte) 0);
+ armorStandDataWatcher.a(4, (byte) 0);
+
+ DataWatcherUtil.setFlag(armorStandDataWatcher, 5, true);
+
+ armorStandDataWatcher.a(6, (float) 1);
+
+ armorStandDataWatcher.a(7, 0);
+
+ armorStandDataWatcher.a(8, (byte) 0);
+ armorStandDataWatcher.a(9, (byte) 0);
+ armorStandDataWatcher.a(10, (byte) 0);
+
+ DataWatcherUtil.setTypeFlag(armorStandDataWatcher, 10, 1, true);
+ DataWatcherUtil.setTypeFlag(armorStandDataWatcher, 10, 4, false);
+ DataWatcherUtil.setTypeFlag(armorStandDataWatcher, 10, 8, false);
+ DataWatcherUtil.setTypeFlag(armorStandDataWatcher, 10, 10, true);
+
+ armorStandDataWatcher.a(11, PacketUtils.ARMOR_STAND_HEAD_POSE);
+ armorStandDataWatcher.a(12, PacketUtils.ARMOR_STAND_BODY_POSE);
+ armorStandDataWatcher.a(13, PacketUtils.ARMOR_STAND_LEFT_ARM_POSE);
+ armorStandDataWatcher.a(14, PacketUtils.ARMOR_STAND_RIGHT_ARM_POSE);
+ armorStandDataWatcher.a(15, PacketUtils.ARMOR_STAND_LEFT_LEG_POSE);
+ armorStandDataWatcher.a(16, PacketUtils.ARMOR_STAND_RIGHT_LEG_POSE);
+ }
+
+ Packet> armorStandPacket;
+
+ if (legacy){
+ EntityWitherSkull skull = new EntityWitherSkull(((CraftWorld) getLocation().getWorld()).getHandle());
+ skull.d(getSupportEntityID());
+ skull.setLocation(getLocation().getX(), (getLocation().getY() + 1.5), getLocation().getZ(), 0.0f, 0.0f);
+ armorStandPacket = new PacketPlayOutSpawnEntity(skull, 66);
+ } else {
+ armorStandPacket = new PacketPlayOutSpawnEntityLiving(
+ getSupportEntityID(),
+ 30,
+ MathHelper.floor(getLocation().getX() * 32.0D),
+ MathHelper.floor((getLocation().getY() + 0.15) * 32.0D),
+ MathHelper.floor(getLocation().getZ() * 32.0D),
+ armorStandDataWatcher
+ );
+ }
+
+ PacketPlayOutAttachEntity attachPacket = new PacketPlayOutAttachEntity(0, getId(), getSupportEntityID());
+
+ PacketUtils.sendPacket(player, spawnPacket);
+ PacketUtils.sendPacket(player, metadataPacket);
+ PacketUtils.sendPacket(player, armorStandPacket);
+ PacketUtils.sendPacket(player, attachPacket);
+ }
+
+ @Override
+ public void sendRePositionPackets(Player player){
+
+ PacketPlayOutEntityTeleport teleportPacket;
+
+ if (PlayerUtil.isLegacy(player)){
+ teleportPacket = new PacketPlayOutEntityTeleport(
+ getSupportEntityID(),
+ MathHelper.floor(getLocation().getX() * 32.0D),
+ MathHelper.floor((getLocation().getY() + 1.5) * 32.0D) ,
+ MathHelper.floor(getLocation().getZ() * 32.0D),
+ (byte) 0,
+ (byte) 0,
+ false
+ );
+ }else{
+ teleportPacket = new PacketPlayOutEntityTeleport(
+ getSupportEntityID(),
+ MathHelper.floor(getLocation().getX() * 32.0D),
+ MathHelper.floor((getLocation().getY() + 0.15) * 32.0D) ,
+ MathHelper.floor(getLocation().getZ() * 32.0D),
+ (byte) 0,
+ (byte) 0,
+ false
+ );
+ }
+
+ PacketUtils.sendPacket(player, teleportPacket);
+ }
+
+ @Override
+ public void sendUpdatePackets(Player player) {
+ PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata();
+
+ packet.setEntityId(getId());
+ packet.setData(Collections.singletonList(new DataWatcher.WatchableObject(5, 10, CraftItemStack.asNMSCopy(itemStack))));
+
+ PacketUtils.sendPacket(player, packet);
+ }
+
+ @Override
+ public void sendDestroyPackets(Player player) {
+ PacketPlayOutEntityDestroy destroyPacket = new PacketPlayOutEntityDestroy(getId(), getSupportEntityID());
+ PacketUtils.sendPacket(player, destroyPacket);
+
+ super.sendDestroyPackets(player);
+ }
+
+ @Override
+ public ItemStack getLine(Player player) {
+ return itemStack;
+ }
+
+ @Override
+ public void setLine(ItemStack line) {
+ this.itemStack = line;
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/lines/impl/MultipleTextLine.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/lines/impl/MultipleTextLine.java
new file mode 100644
index 0000000..9880977
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/lines/impl/MultipleTextLine.java
@@ -0,0 +1,105 @@
+package rip.battle.entity.hologram.lines.impl;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import lombok.Getter;
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import rip.battle.entity.hologram.CraftHologram;
+import rip.battle.entity.hologram.lines.HologramLine;
+
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.function.Function;
+
+@Getter
+public class MultipleTextLine extends HologramLine
> {
+
+ private Function> lines;
+
+ private final Map> textLines = Maps.newHashMap();
+
+ public MultipleTextLine(CraftHologram parent, Location location, Function> lines) {
+ super(parent, location);
+ this.lines = lines;
+ }
+
+ @Override
+ public List getLine(Player player) {
+ return lines.apply(player);
+ }
+
+ @Override
+ public void setLine(List line) {
+ this.lines = player -> line;
+ }
+
+ @Override
+ public void sendSpawnPackets(Player player) {
+ List lines = getLine(player);
+
+ List textLines = this.textLines.get(player.getUniqueId());
+
+ Location location = getLocation().clone();
+
+ if (textLines == null) {
+ textLines = Lists.newArrayList();
+ }
+
+ for (String line : lines) {
+ TextLine textLine = new TextLine(getParent(), location.subtract(0, 0.25, 0), player1 -> line);
+
+ textLine.setMultiLine(true);
+ textLine.spawn(player);
+
+ textLines.add(textLine);
+ }
+
+ this.textLines.put(player.getUniqueId(), textLines);
+ }
+
+ @Override
+ public void updateLocation(Location location) {
+ setLocation(location);
+
+ for (List textLines : this.textLines.values()) {
+
+ Location locationCloned = getLocation().clone();
+
+ for (TextLine textLine : textLines) {
+ textLine.updateLocation(locationCloned.subtract(0, 0.25, 0));
+ }
+ }
+ }
+
+ @Override
+ public void sendDestroyPackets(Player player) {
+
+ List textLines = this.textLines.get(player.getUniqueId());
+
+ if (textLines == null) {
+ return;
+ }
+
+ for (TextLine textLine : textLines) {
+ textLine.sendDestroyPackets(player);
+ }
+
+ this.textLines.remove(player.getUniqueId());
+ }
+
+ @Override
+ public void sendUpdatePackets(Player player) {
+ List textLines = this.textLines.get(player.getUniqueId());
+
+ if (textLines == null) {
+ return;
+ }
+
+ for (TextLine textLine : textLines) {
+ textLine.sendUpdatePackets(player);
+ }
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/lines/impl/SupplierMultipleTextLine.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/lines/impl/SupplierMultipleTextLine.java
new file mode 100644
index 0000000..1c50e5e
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/lines/impl/SupplierMultipleTextLine.java
@@ -0,0 +1,104 @@
+package rip.battle.entity.hologram.lines.impl;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import rip.battle.entity.hologram.CraftHologram;
+import rip.battle.entity.hologram.lines.HologramLine;
+
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public class SupplierMultipleTextLine extends HologramLine>> {
+
+ private Function>> lines;
+
+ private final Map> textLines = Maps.newHashMap();
+
+ public SupplierMultipleTextLine(CraftHologram parent, Location location, Function>> lines) {
+ super(parent, location);
+ this.lines = lines;
+ }
+
+ @Override
+ public Supplier> getLine(Player player) {
+ return lines.apply(player);
+ }
+
+ @Override
+ public void setLine(Supplier> line) {
+ lines = player -> line;
+ }
+
+ @Override
+ public void sendSpawnPackets(Player player) {
+ List lines = getLine(player).get();
+
+ List textLines = this.textLines.get(player.getUniqueId());
+
+ Location location = getLocation().clone();
+
+ if (textLines == null) {
+ textLines = Lists.newArrayList();
+ }
+
+ for (String line : lines) {
+ TextLine textLine = new TextLine(getParent(), location.subtract(0, 0.25, 0), player1 -> line);
+
+ textLine.setMultiLine(true);
+ textLine.spawn(player);
+
+ textLines.add(textLine);
+ }
+
+ this.textLines.put(player.getUniqueId(), textLines);
+ }
+
+ @Override
+ public void updateLocation(Location location) {
+ setLocation(location);
+
+ for (List textLines : this.textLines.values()) {
+
+ Location locationCloned = getLocation().clone();
+
+ for (TextLine textLine : textLines) {
+ textLine.updateLocation(locationCloned.subtract(0, 0.25, 0));
+ }
+ }
+ }
+
+ @Override
+ public void sendDestroyPackets(Player player) {
+
+ List textLines = this.textLines.get(player.getUniqueId());
+
+ if (textLines == null) {
+ return;
+ }
+
+ for (TextLine textLine : textLines) {
+ textLine.sendDestroyPackets(player);
+ }
+
+ this.textLines.remove(player.getUniqueId());
+ }
+
+ @Override
+ public void sendUpdatePackets(Player player) {
+ List textLines = this.textLines.get(player.getUniqueId());
+
+ if (textLines == null) {
+ return;
+ }
+
+ for (TextLine textLine : textLines) {
+ textLine.sendUpdatePackets(player);
+ }
+ }
+}
\ No newline at end of file
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/lines/impl/TextLine.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/lines/impl/TextLine.java
new file mode 100644
index 0000000..30d3b4d
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/lines/impl/TextLine.java
@@ -0,0 +1,164 @@
+package rip.battle.entity.hologram.lines.impl;
+
+import cc.stormworth.core.util.chat.CC;
+import lombok.Getter;
+import lombok.Setter;
+import net.minecraft.server.v1_8_R3.*;
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;
+import org.bukkit.entity.Player;
+import rip.battle.entity.hologram.CraftHologram;
+import rip.battle.entity.hologram.lines.HologramLine;
+import rip.battle.entity.taks.RainbowTask;
+import rip.battle.entity.utils.DataWatcherUtil;
+import rip.battle.entity.utils.PacketUtils;
+import rip.battle.entity.utils.PlayerUtil;
+
+import java.util.Collections;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+@Getter
+@Setter
+public class TextLine extends HologramLine {
+
+ private Function text;
+
+ private boolean multiLine = false;
+
+ public TextLine(CraftHologram parent, Location location, Function text) {
+ this(parent, location, text, null);
+ }
+
+ public TextLine(CraftHologram parent, Location location, Function text, Consumer onClick) {
+ this(parent, location, text, onClick, player -> true);
+ }
+ public TextLine(CraftHologram parent, Location location, Function text, Consumer onClick, Predicate shouldSend) {
+ super(parent, location, shouldSend, onClick);
+ this.text = text;
+ setPersistent(false);
+ }
+
+ @Override
+ public String getLine(Player player) {
+ return text.apply(player);
+ }
+
+ @Override
+ public void setLine(String line) {
+ this.text = player -> line;
+ }
+
+ @Override
+ public void sendSpawnPackets(Player player) {
+
+ boolean legacy = PlayerUtil.isLegacy(player);
+
+ String text = processPlaceholders(getLine(player), player);
+
+ DataWatcher dataWatcher = DataWatcherUtil.createDataWatcher();
+
+ dataWatcher.a(0, (byte) 0);
+ dataWatcher.a(1, (short) 0);
+ dataWatcher.a(2, text);
+ dataWatcher.a(3, text.isEmpty() || text.equalsIgnoreCase("&7") || text.equalsIgnoreCase(" ") ? (byte) 0 : (byte) 1);
+ dataWatcher.a(4, (byte) 0);
+ if (legacy){
+ dataWatcher.a(16, (byte) -2);
+ }else{
+ DataWatcherUtil.setFlag(dataWatcher, 5, true);
+
+ dataWatcher.a(6, (float) 1);
+ dataWatcher.a(7, 0);
+ dataWatcher.a(8, (byte) 0);
+ dataWatcher.a(9, (byte) 0);
+
+ dataWatcher.a(10, (byte) 0);
+
+ DataWatcherUtil.setTypeFlag(dataWatcher, 10, 1, true);
+ DataWatcherUtil.setTypeFlag(dataWatcher, 10, 4, false);
+ DataWatcherUtil.setTypeFlag(dataWatcher, 10, 8, false);
+ DataWatcherUtil.setTypeFlag(dataWatcher, 10, 10, true);
+
+ dataWatcher.a(11, PacketUtils.ARMOR_STAND_HEAD_POSE);
+ dataWatcher.a(12, PacketUtils.ARMOR_STAND_BODY_POSE);
+ dataWatcher.a(13, PacketUtils.ARMOR_STAND_LEFT_ARM_POSE);
+ dataWatcher.a(14, PacketUtils.ARMOR_STAND_RIGHT_ARM_POSE);
+ dataWatcher.a(15, PacketUtils.ARMOR_STAND_LEFT_LEG_POSE);
+ dataWatcher.a(16, PacketUtils.ARMOR_STAND_RIGHT_LEG_POSE);
+ }
+
+ PacketPlayOutSpawnEntityLiving packet = new PacketPlayOutSpawnEntityLiving(
+ getId(),
+ legacy ? 55 : 30,
+ MathHelper.floor(getLocation().getX() * 32.0D),
+ MathHelper.floor(getLocation().getY() * 32.0D),
+ MathHelper.floor(getLocation().getZ() * 32.0D),
+ dataWatcher
+ );
+
+ PacketUtils.sendPacket(player, packet);
+
+ if (legacy){
+ EntityWitherSkull skull = new EntityWitherSkull(((CraftWorld) getLocation().getWorld()).getHandle());
+ skull.d(getSupportEntityID());
+ skull.setLocation(getLocation().getX(), (getLocation().getY() + 3), getLocation().getZ(), 0.0f, 0.0f);
+ PacketUtils.sendPacket(player, new PacketPlayOutSpawnEntity(skull, 66));
+ PacketUtils.sendPacket(player, new PacketPlayOutAttachEntity(0, getId(), getSupportEntityID()));
+ }
+ }
+
+ @Override
+ public void sendUpdatePackets(Player player) {
+ String text = processPlaceholders(getLine(player), player);
+
+ PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata();
+
+ packet.setEntityId(getId());
+ packet.setData(Collections.singletonList(new DataWatcher.WatchableObject(4, 2, text)));
+
+ PacketUtils.sendPacket(player, packet);
+ }
+
+ @Override
+ public void sendRePositionPackets(Player player) {
+ if (PlayerUtil.isLegacy(player)){
+ PacketPlayOutEntityTeleport packet = new PacketPlayOutEntityTeleport(
+ getSupportEntityID(),
+ MathHelper.floor(getLocation().getX() * 32.0D),
+ MathHelper.floor((getLocation().getY() + 3) * 32.0D),
+ MathHelper.floor(getLocation().getZ() * 32.0D),
+ (byte) 0,
+ (byte) 0,
+ false
+ );
+
+ PacketUtils.sendPacket(player, packet);
+ }else{
+ super.sendRePositionPackets(player);
+ }
+ }
+
+ @Override
+ public void sendDestroyPackets(Player player) {
+ PacketUtils.sendPacket(player, new PacketPlayOutEntityDestroy(getSupportEntityID()));
+ PacketUtils.sendPacket(player, new PacketPlayOutEntityDestroy(getId()));
+ }
+
+ public String processPlaceholders(String text, Player player) {
+
+ String textFormatted = text;
+
+ if (textFormatted == null) {
+ return null;
+ }
+
+ textFormatted = textFormatted.replace("{player}", player.getName());
+ textFormatted = textFormatted.replace("{player_displayname}", player.getDisplayName());
+ textFormatted = textFormatted.replace("{rainbow}", RainbowTask.getCurrentColor().toString());
+
+ return CC.translate(textFormatted);
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/menu/HologramEditMenu.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/menu/HologramEditMenu.java
new file mode 100644
index 0000000..bcca593
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/menu/HologramEditMenu.java
@@ -0,0 +1,159 @@
+package rip.battle.entity.hologram.menu;
+
+import cc.stormworth.core.util.inventory.ItemBuilder;
+import cc.stormworth.core.util.menu.Menu;
+import cc.stormworth.core.util.menu.button.Button;
+import cc.stormworth.core.util.menu.button.impl.PlaceholderButton;
+import cc.stormworth.core.util.menu.impl.ConfirmationMenu;
+import com.google.common.collect.Maps;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.DyeColor;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import rip.battle.entity.EntityPlugin;
+import rip.battle.entity.hologram.CraftHologram;
+import rip.battle.entity.hologram.HologramManager;
+import rip.battle.entity.hologram.api.Hologram;
+import rip.battle.entity.npc.api.NPC;
+import rip.battle.entity.npc.menu.NPCEditMenu;
+
+import java.util.Map;
+public class HologramEditMenu extends Menu {
+ private final Hologram hologram;
+ private final HologramManager hologramManager;
+ private final EntityPlugin plugin;
+
+ private final Menu back;
+
+ public HologramEditMenu(EntityPlugin plugin, Hologram hologram, Menu back) {
+ this.plugin = plugin;
+ this.hologramManager = plugin.getHologramManager();
+ this.hologram = hologram;
+ this.back = back;
+ }
+
+ @Override
+ public String getTitle(Player player) {
+ return "&bEditing Hologram &8" + hologram.getName();
+ }
+
+ @Override
+ public int getRows(Player player) {
+ return 3;
+ }
+
+ @Override
+ public Map getButtons(Player player) {
+
+ Map buttons = Maps.newHashMap();
+
+
+ if (back != null){
+ buttons.put(0, new Button() {
+
+ @Override
+ public ItemStack getDisplayItem(Player player) {
+ return new ItemBuilder(Material.BED).setName("&cBack").toItemStack();
+ }
+
+ @Override
+ public void onClick(Player player) {
+ back.open(player);
+ }
+ });
+ }
+
+ buttons.put(getSlot(1, 1), new Button() {
+ @Override
+ public ItemStack getDisplayItem(Player player) {
+ ItemBuilder builder = new ItemBuilder(Material.ENDER_PEARL);
+
+ builder.setName("&6Teleport");
+
+ builder.addLore(
+ "",
+ "&7Click to teleport to this hologram."
+ );
+
+ return builder.toItemStack();
+ }
+
+ @Override
+ public void onClick(Player player) {
+ player.closeInventory();
+ player.teleport(hologram.getLocation());
+ }
+ });
+
+ buttons.put(getSlot(1, 4), new Button() {
+ @Override
+ public ItemStack getDisplayItem(Player player) {
+ ItemBuilder builder = new ItemBuilder(Material.BOOK);
+
+ builder.setName("&6Lines: " + hologram.getLines().size());
+
+ builder.addLore(
+ "",
+ "&7Click to manage lines on this hologram."
+ );
+
+ return builder.toItemStack();
+ }
+
+ @Override
+ public void onClick(Player player) {
+ new HologramManageLinesMenu(plugin, hologram).open(player);
+ }
+ });
+
+
+ buttons.put(getSlot(1, 7), new Button() {
+ @Override
+ public ItemStack getDisplayItem(Player player) {
+ ItemBuilder builder = new ItemBuilder(Material.WOOL);
+
+ builder.setWoolColour(DyeColor.RED);
+
+ builder.setName("&4&lDELETE");
+
+ builder.addLore(
+ "",
+ "&7Click to delete this hologram."
+ );
+
+ return builder.toItemStack();
+ }
+
+ @Override
+ public void onClick(Player player) {
+ new ConfirmationMenu("&4&lAre you sure?", (value) -> {
+
+ if (value){
+ CraftHologram craftHologram = (CraftHologram) hologram;
+
+ if (craftHologram.getParent() != null){
+ NPC npc = (NPC) craftHologram.getParent();
+ npc.setHologram(null);
+ hologram.destroy();
+ new NPCEditMenu(npc, plugin).open(player);
+ }else{
+ hologramManager.removeHologram(hologram.getName());
+ player.closeInventory();
+ }
+
+ }else {
+ new HologramEditMenu(plugin, hologram, null).open(player);
+ }
+
+ }, new PlaceholderButton(new ItemBuilder(Material.WOOL)
+ .setWoolColour(DyeColor.RED)
+ .setName("&4&lDeleting &c" + hologram.getName())
+ .toItemStack()))
+ .open(player);
+ }
+ });
+
+ return buttons;
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/menu/HologramManageLinesMenu.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/menu/HologramManageLinesMenu.java
new file mode 100644
index 0000000..43826c7
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/menu/HologramManageLinesMenu.java
@@ -0,0 +1,149 @@
+package rip.battle.entity.hologram.menu;
+
+import cc.stormworth.core.util.chat.CC;
+import cc.stormworth.core.util.chat.ChatUtils;
+import cc.stormworth.core.util.inventory.ItemBuilder;
+import cc.stormworth.core.util.menu.Menu;
+import cc.stormworth.core.util.menu.button.Button;
+import com.google.common.collect.Maps;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+import rip.battle.entity.EntityPlugin;
+import rip.battle.entity.hologram.CraftHologram;
+import rip.battle.entity.hologram.api.Hologram;
+import rip.battle.entity.hologram.lines.HologramLine;
+import rip.battle.entity.hologram.prompt.HologramAddItemLinePrompt;
+import rip.battle.entity.hologram.prompt.HologramAddLinePrompt;
+import rip.battle.entity.hologram.prompt.HologramEditLinePrompt;
+import rip.battle.entity.npc.api.NPC;
+import rip.battle.entity.npc.menu.NPCEditMenu;
+
+import java.util.Map;
+
+@RequiredArgsConstructor
+public class HologramManageLinesMenu extends Menu {
+
+ private final EntityPlugin plugin;
+ private final Hologram hologram;
+ @Override
+ public String getTitle(Player player) {
+ return "&bEditing lines of &8" + hologram.getName();
+ }
+
+ @Override
+ public int getRows(Player player) {
+ return 3;
+ }
+
+ @Override
+ public Map getButtons(Player player) {
+
+ Map buttons = Maps.newHashMap();
+
+ buttons.put(0, new Button() {
+
+ @Override
+ public ItemStack getDisplayItem(Player player) {
+ return new ItemBuilder(Material.BED).setName("&cBack").toItemStack();
+ }
+
+ @Override
+ public void onClick(Player player) {
+ new HologramEditMenu(plugin, hologram, null).open(player);
+ }
+ });
+
+ buttons.put(getSlot(0, 3), new Button() {
+ @Override
+ public ItemStack getDisplayItem(Player player) {
+
+ ItemBuilder builder = new ItemBuilder(Material.BOOK);
+
+ builder.setName("&6Add line");
+
+ builder.addLore(
+ "",
+ "&7Click to add new line to this hologram."
+ );
+
+ return builder.toItemStack();
+ }
+
+ @Override
+ public void onClick(Player player) {
+ ChatUtils.beginPrompt(player, new HologramAddLinePrompt(plugin, hologram), plugin);
+ }
+ });
+
+ buttons.put(getSlot(0, 5), new Button() {
+ @Override
+ public ItemStack getDisplayItem(Player player) {
+
+ ItemBuilder builder = new ItemBuilder(Material.DIAMOND_PICKAXE);
+
+ builder.setName("&6Add Item line");
+
+ builder.addLore(
+ "",
+ "&7Click to add new item line to this hologram."
+ );
+
+ return builder.toItemStack();
+ }
+
+ @Override
+ public void onClick(Player player) {
+ ChatUtils.beginPrompt(player, new HologramAddItemLinePrompt(plugin, hologram), plugin);
+ }
+ });
+
+ int startSlot = 9;
+
+ for (HologramLine> line : hologram.getLines()) {
+ buttons.put(startSlot++, new Button() {
+ @Override
+ public ItemStack getDisplayItem(Player player) {
+ ItemBuilder builder = new ItemBuilder(Material.PAPER);
+
+ builder.setName("&6" + line.getLine(player));
+
+ builder.addLore(
+ "",
+ "&7Click to edit this line.",
+ "&cShift click to remove this line."
+ );
+
+ return builder.toItemStack();
+ }
+
+ @Override
+ public void onClick(Player player, ClickType clickType) {
+ if (clickType.isShiftClick()) {
+ hologram.removeLine(line);
+ player.sendMessage(CC.translate("&aRemoved line &8" + line.getLine(player) + " &afrom hologram &8" + hologram.getName() + "&a."));
+
+ if (hologram.getLines().isEmpty()){
+
+ CraftHologram craftHologram = (CraftHologram) hologram;
+
+ if (craftHologram.getParent() != null){
+ NPC npc = (NPC) craftHologram.getParent();
+ npc.setHologram(null);
+ hologram.destroy();
+ new NPCEditMenu(npc, plugin).open(player);
+ }
+ }
+
+ } else {
+ ChatUtils.beginPrompt(player, new HologramEditLinePrompt(plugin, hologram, line), plugin);
+ }
+ }
+ });
+ }
+
+ return buttons;
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/prompt/HologramAddItemLinePrompt.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/prompt/HologramAddItemLinePrompt.java
new file mode 100644
index 0000000..00311bc
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/prompt/HologramAddItemLinePrompt.java
@@ -0,0 +1,52 @@
+package rip.battle.entity.hologram.prompt;
+
+import cc.stormworth.core.util.chat.CC;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.Material;
+import org.bukkit.Sound;
+import org.bukkit.conversations.ConversationContext;
+import org.bukkit.conversations.Prompt;
+import org.bukkit.conversations.StringPrompt;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import rip.battle.entity.EntityPlugin;
+import rip.battle.entity.hologram.api.Hologram;
+import rip.battle.entity.hologram.menu.HologramManageLinesMenu;
+
+@RequiredArgsConstructor
+public class HologramAddItemLinePrompt extends StringPrompt {
+
+ private final EntityPlugin plugin;
+ private final Hologram hologram;
+
+ @Override
+ public String getPromptText(ConversationContext conversationContext) {
+ return CC.translate("&7Enter &aconfirm&7 to set your item in hand as the line or type &ccancel &7to cancel.");
+ }
+
+ @Override
+ public Prompt acceptInput(ConversationContext context, String input) {
+
+ Player player = (Player) context.getForWhom();
+
+ if (input.equalsIgnoreCase("cancel")) {
+ new HologramManageLinesMenu(plugin, hologram).open(player);
+ return Prompt.END_OF_CONVERSATION;
+ }
+
+ ItemStack itemStack = player.getItemInHand();
+
+ if (itemStack == null || itemStack.getType() == Material.AIR) {
+ player.sendMessage(CC.translate("&cYou must be holding an item to set it as a hologram line."));
+ player.playSound(player.getLocation(), Sound.NOTE_BASS, 1, 1);
+ return this;
+ }
+
+ hologram.addLine(itemStack);
+
+ player.sendMessage(CC.translate("&aAdded line to hologram."));
+ player.playSound(player.getLocation(), Sound.NOTE_PLING, 1, 1);
+ new HologramManageLinesMenu(plugin, hologram).open(player);
+ return Prompt.END_OF_CONVERSATION;
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/prompt/HologramAddLinePrompt.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/prompt/HologramAddLinePrompt.java
new file mode 100644
index 0000000..7c27e44
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/prompt/HologramAddLinePrompt.java
@@ -0,0 +1,42 @@
+package rip.battle.entity.hologram.prompt;
+
+import cc.stormworth.core.util.chat.CC;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.Sound;
+import org.bukkit.conversations.ConversationContext;
+import org.bukkit.conversations.Prompt;
+import org.bukkit.conversations.StringPrompt;
+import org.bukkit.entity.Player;
+import rip.battle.entity.EntityPlugin;
+import rip.battle.entity.hologram.api.Hologram;
+import rip.battle.entity.hologram.menu.HologramManageLinesMenu;
+
+@RequiredArgsConstructor
+public class HologramAddLinePrompt extends StringPrompt {
+
+ private final EntityPlugin plugin;
+ private final Hologram hologram;
+
+ @Override
+ public String getPromptText(ConversationContext conversationContext) {
+ return CC.translate("&7Enter the text for the new line or type &ccancel &7to cancel.");
+ }
+
+ @Override
+ public Prompt acceptInput(ConversationContext context, String input) {
+
+ Player player = (Player) context.getForWhom();
+
+ if (input.equalsIgnoreCase("cancel")) {
+ new HologramManageLinesMenu(plugin, hologram).open(player);
+ return Prompt.END_OF_CONVERSATION;
+ }
+
+ hologram.addLine(input);
+
+ player.sendMessage(CC.translate("&aAdded line to hologram."));
+ player.playSound(player.getLocation(), Sound.NOTE_PLING, 1, 1);
+ new HologramManageLinesMenu(plugin, hologram).open(player);
+ return Prompt.END_OF_CONVERSATION;
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/prompt/HologramEditLinePrompt.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/prompt/HologramEditLinePrompt.java
new file mode 100644
index 0000000..eea52a9
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/prompt/HologramEditLinePrompt.java
@@ -0,0 +1,66 @@
+package rip.battle.entity.hologram.prompt;
+
+import cc.stormworth.core.util.chat.CC;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.conversations.ConversationContext;
+import org.bukkit.conversations.Prompt;
+import org.bukkit.conversations.StringPrompt;
+import org.bukkit.entity.Player;
+import rip.battle.entity.EntityPlugin;
+import rip.battle.entity.hologram.api.Hologram;
+import rip.battle.entity.hologram.lines.HologramLine;
+import rip.battle.entity.hologram.lines.impl.ItemLine;
+import rip.battle.entity.hologram.lines.impl.TextLine;
+import rip.battle.entity.hologram.menu.HologramManageLinesMenu;
+
+@RequiredArgsConstructor
+public class HologramEditLinePrompt extends StringPrompt {
+
+ private final EntityPlugin plugin;
+ private final Hologram hologram;
+ private final HologramLine> line;
+
+ @Override
+ public String getPromptText(ConversationContext context) {
+
+ if (line instanceof ItemLine){
+ return CC.translate("&7Enter &aconfirm&7 to set item in hand as the item for this line or type &ccancel &7to cancel.");
+ }
+
+ return CC.translate("&7Enter the text for the line or type &ccancel &7to cancel.");
+ }
+
+ @Override
+ public Prompt acceptInput(ConversationContext context, String input) {
+
+ Player player = (Player) context.getForWhom();
+
+ if (input.equalsIgnoreCase("cancel")) {
+ new HologramManageLinesMenu(plugin, hologram).open(player);
+ return Prompt.END_OF_CONVERSATION;
+ }
+
+ if (line instanceof ItemLine){
+ if (input.equalsIgnoreCase("confirm")){
+ ((ItemLine) line).setLine(player.getItemInHand());
+
+ line.updateForCurrentWatchers();
+ player.sendMessage(CC.translate("&aSet item in hand as the item for this line."));
+
+ new HologramManageLinesMenu(plugin, hologram).open(player);
+ return Prompt.END_OF_CONVERSATION;
+ }
+ }else{
+ TextLine textLine = (TextLine) line;
+
+ textLine.setLine(input);
+ textLine.updateForCurrentWatchers();
+
+ player.sendMessage(CC.translate("&aSet text for this line."));
+ new HologramManageLinesMenu(plugin, hologram).open(player);
+ return Prompt.END_OF_CONVERSATION;
+ }
+
+ return this;
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/runnable/HologramUpdateRunnable.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/runnable/HologramUpdateRunnable.java
new file mode 100644
index 0000000..7d6378d
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/runnable/HologramUpdateRunnable.java
@@ -0,0 +1,28 @@
+package rip.battle.entity.hologram.runnable;
+
+import lombok.RequiredArgsConstructor;
+import rip.battle.entity.hologram.api.Hologram;
+import rip.battle.entity.hologram.HologramManager;
+import rip.battle.entity.hologram.impl.UpdatableHologram;
+
+@RequiredArgsConstructor
+public class HologramUpdateRunnable implements Runnable {
+
+ private final HologramManager hologramManager;
+
+ @Override
+ public void run() {
+ for (Hologram hologram : hologramManager.getHolograms().values()) {
+ if (hologram instanceof UpdatableHologram){
+ UpdatableHologram updatableHologram = (UpdatableHologram) hologram;
+
+ if (updatableHologram.canUpdate() && !updatableHologram.getLines().isEmpty()){
+
+ updatableHologram.getCurrentWatchersPlayers().forEach(updatableHologram::update);
+
+ updatableHologram.setLastUpdate(System.currentTimeMillis());
+ }
+ }
+ }
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/hologram/serialization/HologramTypeAdapter.java b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/serialization/HologramTypeAdapter.java
new file mode 100644
index 0000000..a19f386
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/hologram/serialization/HologramTypeAdapter.java
@@ -0,0 +1,92 @@
+package rip.battle.entity.hologram.serialization;
+
+import cc.stormworth.core.util.location.LocationUtils;
+import cc.stormworth.core.util.serialize.ItemStackAdapter;
+import com.google.gson.*;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import rip.battle.entity.hologram.CraftHologram;
+import rip.battle.entity.hologram.api.Hologram;
+import rip.battle.entity.hologram.impl.UpdatableHologram;
+import rip.battle.entity.hologram.lines.HologramLine;
+import rip.battle.entity.hologram.lines.impl.ItemLine;
+import rip.battle.entity.hologram.lines.impl.TextLine;
+
+import java.lang.reflect.Type;
+
+public class HologramTypeAdapter implements JsonDeserializer, JsonSerializer {
+
+ @Override
+ public Hologram deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
+ return fromJson(jsonElement);
+ }
+
+ @Override
+ public JsonElement serialize(Hologram hologram, Type type, JsonSerializationContext jsonSerializationContext) {
+ return toJson(hologram);
+ }
+
+ public static JsonObject toJson(Hologram hologram) {
+ if (hologram == null) {
+ return null;
+ }
+
+ JsonObject object = new JsonObject();
+
+ CraftHologram craftHologram = (CraftHologram) hologram;
+
+ object.addProperty("location", LocationUtils.serializeLocation(craftHologram.getLocation()));
+
+ object.addProperty("name", craftHologram.getName());
+
+ JsonArray lines = new JsonArray();
+
+ for (HologramLine> line : craftHologram.getLines()) {
+ JsonObject lineObject = new JsonObject();
+
+ if (line instanceof TextLine) {
+ lineObject.addProperty("text", ((TextLine) line).getText().apply(Bukkit.getConsoleSender()));
+ }else {
+ ItemLine itemLine = (ItemLine) line;
+ lineObject.add("item", ItemStackAdapter.serialize(itemLine.getItemStack()));
+ }
+
+ lines.add(lineObject);
+ }
+
+ object.add("lines", lines);
+
+ object.add("hidden", new JsonPrimitive(craftHologram.isHidden()));
+
+ return object;
+ }
+
+ public static Hologram fromJson(JsonElement jsonElement) {
+ if (jsonElement == null || !jsonElement.isJsonObject()) {
+ return null;
+ }
+
+ JsonObject jsonObject = jsonElement.getAsJsonObject();
+
+ Location location = LocationUtils.deserializeLocation(jsonObject.get("location").getAsString());
+
+ JsonArray lines = jsonObject.getAsJsonArray("lines");
+
+ String name = jsonObject.get("name").getAsString();
+ CraftHologram hologram = new UpdatableHologram(name, location);
+
+ for (JsonElement line : lines) {
+ JsonObject lineObject = line.getAsJsonObject();
+
+ if (lineObject.has("text")) {
+ hologram.addLine(lineObject.get("text").getAsString());
+ }else {
+ hologram.addLine(ItemStackAdapter.deserialize(lineObject.get("item")));
+ }
+ }
+
+ hologram.updateVisibility(jsonObject.get("hidden").getAsBoolean());
+
+ return hologram;
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/listeners/PlayerListener.java b/BattleEntity-main/src/main/java/rip/battle/entity/listeners/PlayerListener.java
new file mode 100644
index 0000000..823f8d9
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/listeners/PlayerListener.java
@@ -0,0 +1,26 @@
+package rip.battle.entity.listeners;
+
+import lombok.RequiredArgsConstructor;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import rip.battle.entity.Entity;
+import rip.battle.entity.EntityManager;
+import rip.battle.entity.hologram.CraftHologram;
+import rip.battle.entity.hologram.HologramManager;
+import rip.battle.entity.npc.NPCManager;
+
+@RequiredArgsConstructor
+public class PlayerListener implements Listener {
+ @EventHandler
+ public void onQuit(PlayerQuitEvent event){
+ Player player = event.getPlayer();
+
+ for (Entity entity : EntityManager.getEntities().values()){
+ entity.destroy(player);
+ }
+ }
+
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/living/LivingEntity.java b/BattleEntity-main/src/main/java/rip/battle/entity/living/LivingEntity.java
new file mode 100644
index 0000000..f1625e8
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/living/LivingEntity.java
@@ -0,0 +1,191 @@
+package rip.battle.entity.living;
+
+import cc.stormworth.core.util.TaskUtil;
+import lombok.Getter;
+import lombok.Setter;
+import net.minecraft.server.v1_8_R3.MathHelper;
+import net.minecraft.server.v1_8_R3.PacketPlayOutEntityTeleport;
+import net.minecraft.server.v1_8_R3.PacketPlayOutEntityVelocity;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.bukkit.util.Vector;
+import rip.battle.entity.Entity;
+import rip.battle.entity.EntityPlugin;
+import rip.battle.entity.living.events.LivingEntityDeathEvent;
+import rip.battle.entity.tick.TickableEntity;
+import rip.battle.entity.utils.AngleUtils;
+import rip.battle.entity.utils.PacketUtils;
+import rip.battle.spigot.Battle;
+import rip.battle.spigot.knockback.KnockbackProfile;
+
+@Getter @Setter
+public abstract class LivingEntity extends Entity implements TickableEntity {
+
+ private double health = 20;
+ private double maxHealth = 20;
+
+ private boolean dead;
+
+ private boolean invulnerable;
+ private long lastDamage;
+
+ private boolean gravity = true;
+
+ private String customName;
+
+ private Vector velocity;
+
+ private boolean onGround;
+
+ public LivingEntity(Location location) {
+ super(location);
+ velocity = new Vector();
+ }
+
+ @Override
+ public void tick() {
+ if (gravity){
+ if (!getLocation().getBlock().getType().isSolid()) {
+
+ System.out.println("Falling");
+ setVelocity(getVelocity().clone().add(new Vector(0, -0.1, 0)));
+
+ onGround = false;
+ }else{
+ System.out.println("On ground");
+ onGround = true;
+ }
+ }
+ }
+
+ public void setVelocity(Vector vector) {
+ this.velocity = vector;
+ move(vector);
+
+ getCurrentWatchersPlayers().forEach(currentWatchersPlayer -> sendVelocityPacket(currentWatchersPlayer, vector));
+ }
+
+ public void sendVelocityPacket(Player player, Vector vector) {
+ PacketPlayOutEntityVelocity packet = new PacketPlayOutEntityVelocity(getId(), vector.getX(), vector.getY(), vector.getZ());
+ PacketUtils.sendPacket(player, packet);
+ }
+
+ public void move(Vector vector) {
+ Location to = getLocation().clone().add(vector);
+
+ if (to.getBlock().getRelative(0, 1, 0).getType().isSolid()) {
+ to.setY(to.getBlockY() + 1);
+ }
+
+ setLocation(to);
+
+ getCurrentWatchersPlayers().forEach(this::sendMovePacket);
+ }
+
+ public void sendMovePacket(Player player) {
+ PacketPlayOutEntityTeleport teleportPacket = new PacketPlayOutEntityTeleport(
+ getId(),
+ MathHelper.floor(getLocation().getX() * 32.0D),
+ MathHelper.floor(getLocation().getY() * 32.0D),
+ MathHelper.floor(getLocation().getZ() * 32.0D),
+ AngleUtils.yawToBytes(getLocation().getYaw()),
+ AngleUtils.yawToBytes(getLocation().getPitch()),
+ true);
+
+ PacketUtils.sendPacket(player, teleportPacket);
+
+ velocity = new Vector();
+ }
+
+ @Override
+ public void onLeftClick(Player player) {
+
+ ItemStack hand = player.getItemInHand();
+
+ double damage = 1;
+
+ switch (hand.getType()){
+ case WOOD_SWORD:
+ case STONE_SWORD:
+ damage = 3.0;
+ break;
+ case IRON_SWORD:
+ damage = 4.0;
+ break;
+ case GOLD_SWORD:
+ damage = 5.0;
+ break;
+ case DIAMOND_SWORD:
+ damage = 6.0;
+ break;
+ }
+
+ //Change this xd
+ double finalDamage = damage;
+ TaskUtil.run(JavaPlugin.getPlugin(EntityPlugin.class), () -> {
+ damage(player, finalDamage);
+ });
+ }
+
+ public boolean damage(org.bukkit.entity.Entity entity, double damage){
+
+ if (invulnerable){
+ return false;
+ }
+
+ if (lastDamage + 500L > System.currentTimeMillis()){
+ return false;
+ }
+
+ if (dead){
+ return false;
+ }
+
+ health -= damage;
+
+ if (health <= 0){
+ onDeath(entity);
+ sendStatusPacket((byte) 3);
+
+ TaskUtil.runLater(JavaPlugin.getPlugin(EntityPlugin.class), () -> {
+ if (dead){
+ destroyForCurrentWatchers();
+ }
+ }, 40);
+
+
+ Bukkit.getPluginManager().callEvent(new LivingEntityDeathEvent(this, entity));
+ return true;
+ }
+
+ lastDamage = System.currentTimeMillis();
+
+ sendAnimationPacket(1);
+
+ KnockbackProfile profile = Battle.INSTANCE.getConfig().getCurrentKb();
+
+ setVelocity(new Vector(
+ (-MathHelper.sin(entity.getLocation().getYaw() * 3.1415927F / 180.0F) * profile.getHorizontal()),
+ profile.getVertical(),
+ (MathHelper.cos(entity.getLocation().getYaw() * 3.1415927F / 180.0F) * profile.getHorizontal())
+ ));
+ return true;
+ }
+
+ public void onDeath(org.bukkit.entity.Entity entity){
+ dead = true;
+ }
+
+ public void setCustomName(String customName) {
+ this.customName = customName;
+
+ updateForCurrentWatchers();
+ }
+
+ public boolean isCollideWithBlock(Location to){
+ return to.getBlock().getType().isSolid();
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/living/events/LivingEntityDeathEvent.java b/BattleEntity-main/src/main/java/rip/battle/entity/living/events/LivingEntityDeathEvent.java
new file mode 100644
index 0000000..cbe6fc0
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/living/events/LivingEntityDeathEvent.java
@@ -0,0 +1,26 @@
+package rip.battle.entity.living.events;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+import rip.battle.entity.living.LivingEntity;
+
+@RequiredArgsConstructor
+@Getter
+public class LivingEntityDeathEvent extends Event {
+
+ private static final HandlerList handlers = new HandlerList();
+
+ private final LivingEntity entity;
+ private final org.bukkit.entity.Entity killer;
+
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/living/impl/VillagerEntity.java b/BattleEntity-main/src/main/java/rip/battle/entity/living/impl/VillagerEntity.java
new file mode 100644
index 0000000..1e55fd5
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/living/impl/VillagerEntity.java
@@ -0,0 +1,83 @@
+package rip.battle.entity.living.impl;
+
+import lombok.Getter;
+import lombok.Setter;
+import net.minecraft.server.v1_8_R3.*;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import rip.battle.entity.living.LivingEntity;
+import rip.battle.entity.utils.AngleUtils;
+import rip.battle.entity.utils.DataWatcherUtil;
+import rip.battle.entity.utils.PacketUtils;
+
+import java.util.Collections;
+
+@Setter @Getter
+public class VillagerEntity extends LivingEntity {
+
+ public VillagerEntity(Location location) {
+ super(location);
+ }
+
+ @Override
+ public String getTypeName() {
+ return "Villager";
+ }
+
+ @Override
+ public void sendSpawnPackets(Player player) {
+ DataWatcher dataWatcher = DataWatcherUtil.createDataWatcher();
+
+ dataWatcher.a(0, (byte) 0);
+ dataWatcher.a(1, (short) 300);
+ dataWatcher.a(2, getCustomName() == null ? "" : getCustomName());
+ dataWatcher.a(3, (byte) 1);
+ dataWatcher.a(4, (byte) 1);
+ dataWatcher.a(6, (float) 1);
+ dataWatcher.a(10, (byte) 2);
+ dataWatcher.a(12, (byte) 0);
+ dataWatcher.a(16, 0);
+
+ PacketPlayOutSpawnEntityLiving spawnPacket = new PacketPlayOutSpawnEntityLiving(
+ getId(),
+ 120,
+ MathHelper.floor(getLocation().getX() * 32.0D),
+ MathHelper.floor(getLocation().getY() * 32.0D),
+ MathHelper.floor(getLocation().getZ() * 32.0D),
+ dataWatcher);
+
+ PacketPlayOutEntity.PacketPlayOutEntityLook lookPacket = new PacketPlayOutEntity.PacketPlayOutEntityLook(
+ getId(),
+ AngleUtils.yawToBytes(getLocation().getYaw()),
+ AngleUtils.yawToBytes(getLocation().getPitch()),
+ true);
+
+ PacketPlayOutEntityHeadRotation headRotationPacket = new PacketPlayOutEntityHeadRotation(
+ getId(),
+ AngleUtils.yawToBytes(getLocation().getYaw()));
+
+ PacketPlayOutEntityTeleport teleportPacket = new PacketPlayOutEntityTeleport(
+ getId(),
+ MathHelper.floor(getLocation().getX() * 32.0D),
+ MathHelper.floor(getLocation().getY() * 32.0D),
+ MathHelper.floor(getLocation().getZ() * 32.0D),
+ AngleUtils.yawToBytes(getLocation().getYaw()),
+ AngleUtils.yawToBytes(getLocation().getPitch()),
+ true);
+
+ PacketUtils.sendPacket(player, spawnPacket);
+ PacketUtils.sendPacket(player, lookPacket);
+ PacketUtils.sendPacket(player, headRotationPacket);
+ PacketUtils.sendPacket(player, teleportPacket);
+ }
+
+ @Override
+ public void sendUpdatePackets(Player player) {
+ PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata();
+
+ packet.setEntityId(getId());
+ packet.setData(Collections.singletonList(new DataWatcher.WatchableObject(4, 2, getCustomName())));
+
+ PacketUtils.sendPacket(player, packet);
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/npc/NPCBuilder.java b/BattleEntity-main/src/main/java/rip/battle/entity/npc/NPCBuilder.java
new file mode 100644
index 0000000..ccbb4c4
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/npc/NPCBuilder.java
@@ -0,0 +1,196 @@
+package rip.battle.entity.npc;
+
+import cc.stormworth.core.util.UUIDUtils;
+import cc.stormworth.core.util.skin.SkinTexture;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.entity.NPC;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import rip.battle.entity.hologram.api.Hologram;
+
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+
+public class NPCBuilder {
+
+ public NPCEntity npc;
+
+ public NPCBuilder(String name, Location location) {
+ npc = new NPCEntity(name, location);
+ }
+
+ public NPCBuilder setSkin(SkinTexture skin) {
+ npc.setSkin(skin);
+ return this;
+ }
+
+ public NPCBuilder setSkin(Player player) {
+ npc.setSkin(SkinTexture.getPlayerTexture(player));
+ return this;
+ }
+
+ public NPCBuilder setSkin(String value, String signature) {
+ npc.setSkin(new SkinTexture(value, signature));
+ return this;
+ }
+
+ public NPCBuilder setSkin(String name) {
+ UUID uuid = Bukkit.getOfflinePlayer(name).getUniqueId();
+
+ if (uuid == null){
+ CompletableFuture future = UUIDUtils.getUUIDFromMojang(name);
+
+ future.thenAccept(uuidFetch -> {
+ if (uuidFetch == null){
+ return;
+ }
+
+ CompletableFuture skinFuture = UUIDUtils.getSkinFromMojang(uuidFetch);
+
+ skinFuture.thenAccept(skinFetch -> {
+ if (skinFetch == null){
+ return;
+ }
+
+ npc.setSkin(skinFetch);
+ });
+ });
+ }else {
+ CompletableFuture skinFuture = UUIDUtils.getSkinFromMojang(uuid);
+
+ skinFuture.thenAccept(skinFetch -> {
+ if (skinFetch == null){
+ return;
+ }
+
+ npc.setSkin(skinFetch);
+ });
+ }
+ return this;
+ }
+
+ public NPCBuilder setDisplayName(String displayName) {
+ npc.setDisplayName(displayName);
+ return this;
+ }
+
+ public NPCBuilder setArmor(Player player) {
+
+ if (player.getInventory().getHelmet() != null) {
+ npc.setHelmet(player.getInventory().getHelmet());
+ }
+
+ if (player.getInventory().getChestplate() != null) {
+ npc.setChestplate(player.getInventory().getChestplate());
+ }
+
+ if (player.getInventory().getLeggings() != null) {
+ npc.setLeggings(player.getInventory().getLeggings());
+ }
+
+ if (player.getInventory().getBoots() != null) {
+ npc.setBoots(player.getInventory().getBoots());
+ }
+
+ return this;
+ }
+
+ public NPCBuilder setArmor(ItemStack[] armor){
+ npc.setArmor(armor);
+ return this;
+ }
+
+ public NPCBuilder setHelmet(ItemStack helmet) {
+ npc.setHelmet(helmet);
+ return this;
+ }
+
+ public NPCBuilder setChestplate(ItemStack chestplate) {
+ npc.setChestplate(chestplate);
+ return this;
+ }
+
+ public NPCBuilder setLeggings(ItemStack leggings) {
+ npc.setLeggings(leggings);
+ return this;
+ }
+
+ public NPCBuilder setBoots(ItemStack boots) {
+ npc.setBoots(boots);
+ return this;
+ }
+
+ public NPCBuilder setItemInHand(ItemStack itemInHand) {
+ npc.setItemInHand(itemInHand);
+ return this;
+ }
+
+ public NPCBuilder onInteract(Consumer consumer) {
+ npc.setOnLeftClick(consumer);
+ return this;
+ }
+
+ public NPCBuilder onAttack(Consumer consumer) {
+ npc.setOnRightClick(consumer);
+ return this;
+ }
+
+ public NPCBuilder setHologram(Hologram hologram) {
+ npc.setHologram(hologram);
+ return this;
+ }
+
+ public NPCBuilder setCommands(Set commands) {
+ npc.setCommands(commands);
+ return this;
+ }
+
+ public NPCBuilder addCommand(String command) {
+ npc.getCommands().add(command);
+ return this;
+ }
+
+ public NPCBuilder removeCommand(String command) {
+ npc.getCommands().remove(command);
+ return this;
+ }
+
+ public NPCBuilder setMessages(List messages) {
+ npc.setMessages(messages);
+ return this;
+ }
+
+ public NPCBuilder addMessage(String message) {
+ npc.getMessages().add(message);
+ return this;
+ }
+
+ public NPCBuilder removeMessage(String message) {
+ npc.getMessages().remove(message);
+ return this;
+ }
+
+ public NPCBuilder setMessage(int index, String message) {
+ npc.getMessages().set(index, message);
+ return this;
+ }
+
+ public NPCBuilder setOnRightClick(Consumer onRightClick) {
+ npc.setOnRightClick(onRightClick);
+ return this;
+ }
+
+ public NPCBuilder setOnLeftClick(Consumer onLeftClick) {
+ npc.setOnLeftClick(onLeftClick);
+ return this;
+ }
+
+ public NPCEntity build() {
+ return npc;
+ }
+
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/npc/NPCEntity.java b/BattleEntity-main/src/main/java/rip/battle/entity/npc/NPCEntity.java
new file mode 100644
index 0000000..a1a7987
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/npc/NPCEntity.java
@@ -0,0 +1,467 @@
+package rip.battle.entity.npc;
+
+import cc.stormworth.core.util.TaskUtil;
+import cc.stormworth.core.util.chat.CC;
+import cc.stormworth.core.util.skin.SkinTexture;
+import com.google.common.collect.Lists;
+import com.mojang.authlib.GameProfile;
+import com.mojang.authlib.properties.Property;
+import lombok.Getter;
+import lombok.Setter;
+import net.minecraft.server.v1_8_R3.*;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftItemStack;
+import org.bukkit.craftbukkit.v1_8_R3.scoreboard.CraftScoreboard;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.PlayerInventory;
+import org.bukkit.plugin.java.JavaPlugin;
+import rip.battle.entity.Entity;
+import rip.battle.entity.EntityPlugin;
+import rip.battle.entity.hologram.CraftHologram;
+import rip.battle.entity.hologram.api.Hologram;
+import rip.battle.entity.npc.api.NPC;
+import rip.battle.entity.utils.AngleUtils;
+import rip.battle.entity.utils.DataWatcherUtil;
+import rip.battle.entity.utils.PacketUtils;
+import rip.battle.entity.utils.PlayerUtil;
+
+import java.util.*;
+
+@Getter @Setter
+public class NPCEntity extends Entity implements NPC {
+
+ private final UUID uuid;
+ private String name;
+ private String displayName;
+
+ private GameProfile gameProfile;
+
+ private SkinTexture skin;
+
+ private CraftHologram hologram;
+
+ private boolean hidden;
+
+ private ItemStack itemInHand;
+
+ private ItemStack helmet;
+ private ItemStack chestplate;
+ private ItemStack leggings;
+ private ItemStack boots;
+
+ private List messages = Lists.newArrayList();
+
+ private int supportEntityID;
+
+ public NPCEntity(String name, String displayName, Location location) {
+ super(location);
+ this.name = name;
+ this.displayName = CC.translate(displayName);
+ this.uuid = UUID.randomUUID();
+
+ this.gameProfile = new GameProfile(uuid, displayName);
+ }
+
+ public NPCEntity(String name, Location location) {
+ this(name, name, location);
+ }
+
+ @Override
+ public String getTypeName() {
+ return "NPC";
+ }
+
+ @Override
+ public Hologram getAttachedHologram() {
+ return hologram;
+ }
+
+ @Override
+ public void onRightClick(Player player) {
+
+ super.onRightClick(player);
+
+ if (!getCommands().isEmpty()){
+ getCommands().forEach(command -> player.performCommand(command.replace("%player%", player.getName())));
+ }
+
+ if (!messages.isEmpty()) {
+ messages.forEach(player::sendMessage);
+ }
+ }
+
+ @Override
+ public void sendSpawnPackets(Player player) {
+
+ DataWatcher dataWatcher = DataWatcherUtil.createNPCDataWatcher(this);
+
+ PacketPlayOutPlayerInfo addPlayerPacket = new PacketPlayOutPlayerInfo(
+ PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER,
+ gameProfile,
+ 0,
+ WorldSettings.EnumGamemode.SURVIVAL,
+ null);
+
+ PacketPlayOutPlayerInfo removePlayerPacket = new PacketPlayOutPlayerInfo(
+ PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER,
+ gameProfile,
+ 0,
+ WorldSettings.EnumGamemode.SURVIVAL,
+ null);
+
+ PacketPlayOutNamedEntitySpawn spawnPacket = new PacketPlayOutNamedEntitySpawn(
+ getId(),
+ uuid,
+ MathHelper.floor(getLocation().getX() * 32.0),
+ MathHelper.floor(getLocation().getY() * 32.0),
+ MathHelper.floor(getLocation().getZ() * 32.0),
+ AngleUtils.yawToBytes(getLocation().getYaw()),
+ AngleUtils.yawToBytes(getLocation().getPitch()),
+ 0,
+ dataWatcher
+ );
+
+ PacketPlayOutEntity.PacketPlayOutEntityLook lookPacket = new PacketPlayOutEntity.PacketPlayOutEntityLook(
+ getId(),
+ AngleUtils.yawToBytes(getLocation().getYaw()),
+ AngleUtils.yawToBytes(getLocation().getPitch()),
+ true
+ );
+
+ PacketPlayOutEntityHeadRotation headRotationPacket = new PacketPlayOutEntityHeadRotation(
+ getId(),
+ AngleUtils.yawToBytes(getLocation().getYaw())
+ );
+
+ PacketPlayOutEntityTeleport teleportPacket = new PacketPlayOutEntityTeleport(
+ getId(),
+ MathHelper.floor(getLocation().getX() * 32.0),
+ MathHelper.floor(getLocation().getY() * 32.0),
+ MathHelper.floor(getLocation().getZ() * 32.0),
+ AngleUtils.yawToBytes(getLocation().getYaw()),
+ AngleUtils.yawToBytes(getLocation().getPitch()),
+ true
+ );
+
+ PacketUtils.sendPacket(player, addPlayerPacket);
+
+ TaskUtil.runLater(JavaPlugin.getPlugin(EntityPlugin.class), () -> {
+
+ PacketUtils.sendPacket(player, spawnPacket);
+
+ if (itemInHand != null) {
+ PacketUtils.sendPacket(player, new PacketPlayOutEntityEquipment(getId(), 0, CraftItemStack.asNMSCopy(itemInHand)));
+ }
+
+ if (helmet != null) {
+ PacketUtils.sendPacket(player, new PacketPlayOutEntityEquipment(getId(), 4, CraftItemStack.asNMSCopy(helmet)));
+ }
+
+ if (chestplate != null) {
+ PacketUtils.sendPacket(player, new PacketPlayOutEntityEquipment(getId(), 3, CraftItemStack.asNMSCopy(chestplate)));
+ }
+
+ if (leggings != null) {
+ PacketUtils.sendPacket(player, new PacketPlayOutEntityEquipment(getId(), 2, CraftItemStack.asNMSCopy(leggings)));
+ }
+
+ if (boots != null) {
+ PacketUtils.sendPacket(player, new PacketPlayOutEntityEquipment(getId(), 1, CraftItemStack.asNMSCopy(boots)));
+ }
+
+ TaskUtil.runLater(JavaPlugin.getPlugin(EntityPlugin.class), () -> {
+ PacketUtils.sendPacket(player, lookPacket);
+ PacketUtils.sendPacket(player, headRotationPacket);
+ PacketUtils.sendPacket(player, teleportPacket);
+ PacketUtils.sendPacket(player, new PacketPlayOutAnimation(getId(), 0));
+ PacketUtils.sendPacket(player, removePlayerPacket);
+
+ if (hologram != null) {
+ hideNamePlate(player);
+ }
+
+ }, 4L);
+
+ if (hologram != null){
+ hologram.spawnFor(player);
+ }
+
+ }, 1L);
+ }
+
+ @Override
+ public void sendUpdatePackets(Player player) {
+
+ PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata();
+
+ packet.setEntityId(getId());
+ packet.setData(Collections.singletonList(new DataWatcher.WatchableObject(4, 2, displayName)));
+
+ PacketUtils.sendPacket(player, packet);
+ }
+
+ @Override
+ public void updateLocation(Location location) {
+ super.updateLocation(location);
+
+ if (hologram != null){
+ hologram.updateLocation(calculateHologramLocation());
+ }
+
+ getCurrentWatchersPlayers().forEach(player -> {
+
+ PacketUtils.sendPacket(player, new PacketPlayOutEntityHeadRotation(
+ getId(),
+ AngleUtils.yawToBytes(getLocation().getYaw())
+ ));
+
+ PacketUtils.sendPacket(player, new PacketPlayOutAnimation(
+ getId(),
+ 0
+ ));
+ });
+ }
+
+ public Location calculateHologramLocation(){
+ return getLocation().clone().subtract(0, 0.2, 0);
+ }
+
+ public void setSkin(SkinTexture skin){
+ this.skin = skin;
+
+ if (skin != null) {
+ gameProfile = new GameProfile(uuid, displayName);
+
+ gameProfile.getProperties().put("textures", new Property("textures", skin.getValue(), skin.getSignature()));
+ }else {
+ gameProfile = new GameProfile(uuid, displayName);
+
+ gameProfile.getProperties().clear();
+ }
+
+ resendForCurrentWatchers();
+ }
+
+ @Override
+ public void destroy() {
+ onDelete();
+ }
+
+ @Override
+ public void onDelete() {
+ super.onDelete();
+
+ if (hologram != null){
+ hologram.destroy();
+ }
+ }
+
+ @Override
+ public void teleport(double x, double y, double z) {
+ teleport(new Location(getLocation().getWorld(), x, y, z, getLocation().getYaw(), getLocation().getPitch()));
+ }
+
+ @Override
+ public void teleport(double x, double y, double z, float yaw, float pitch) {
+ teleport(new Location(getLocation().getWorld(), x, y, z, yaw, pitch));
+ }
+
+ public void teleport(Location location) {
+ updateLocation(location);
+ }
+
+ @Override
+ public void teleport(Location location, float yaw, float pitch) {
+
+ Location newLocation = location.clone();
+
+ newLocation.setYaw(yaw);
+ newLocation.setPitch(pitch);
+
+ updateLocation(newLocation);
+ }
+
+ protected void updateTexture(SkinTexture skin){
+ this.skin = skin;
+
+ if (skin != null) {
+ gameProfile = new GameProfile(uuid, displayName);
+
+ gameProfile.getProperties().put("textures", new Property("textures", skin.getValue(), skin.getSignature()));
+
+ for (Player player : getCurrentWatchersPlayers()) {
+ destroy(player);
+ spawn(player);
+ }
+ }
+ }
+
+ public void updateItemInHand(ItemStack itemInHand) {
+ this.itemInHand = itemInHand;
+
+ for (Player player : getCurrentWatchersPlayers()){
+ PacketUtils.sendPacket(player, new PacketPlayOutEntityEquipment(getId(), 0, CraftItemStack.asNMSCopy(itemInHand == null ? new ItemStack(Material.AIR) : itemInHand)));
+ }
+ }
+
+ public void setHelmet(ItemStack helmet) {
+ this.helmet = helmet;
+
+ for (Player player : getCurrentWatchersPlayers()){
+ PacketUtils.sendPacket(player, new PacketPlayOutEntityEquipment(getId(), 4, CraftItemStack.asNMSCopy(helmet == null ? new ItemStack(Material.AIR) : helmet)));
+ }
+ }
+
+ public void setChestplate(ItemStack chestplate) {
+ this.chestplate = chestplate;
+
+ for (Player player : getCurrentWatchersPlayers()){
+ PacketUtils.sendPacket(player, new PacketPlayOutEntityEquipment(getId(), 3, CraftItemStack.asNMSCopy(chestplate == null ? new ItemStack(Material.AIR) : chestplate)));
+ }
+ }
+
+ public void setLeggings(ItemStack leggings) {
+ this.leggings = leggings;
+
+ for (Player player : getCurrentWatchersPlayers()){
+ PacketUtils.sendPacket(player, new PacketPlayOutEntityEquipment(getId(), 2, CraftItemStack.asNMSCopy(leggings == null ? new ItemStack(Material.AIR) : leggings)));
+ }
+ }
+
+ public void setBoots(ItemStack boots) {
+ this.boots = boots;
+
+ for (Player player : getCurrentWatchersPlayers()){
+ PacketUtils.sendPacket(player, new PacketPlayOutEntityEquipment(getId(), 1, CraftItemStack.asNMSCopy(boots == null ? new ItemStack(Material.AIR) : boots)));
+ }
+ }
+
+ public void setHologram(Hologram hologram) {
+ this.hologram = (CraftHologram) hologram;
+
+ if (hologram != null){
+ this.hologram.updateLocation(calculateHologramLocation());
+ ((CraftHologram) hologram).setParent(this);
+
+ for (Player player : getCurrentWatchersPlayers()){
+ hideNamePlate(player);
+ this.hologram.spawnFor(player);
+ }
+ }else{
+ for (Player player : getCurrentWatchersPlayers()){
+ showNamePlate(player);
+ }
+ }
+
+ updateForCurrentWatchers();
+ }
+
+ protected void hideNamePlate(Player player){
+ if (PlayerUtil.isLegacy(player)){
+ if (supportEntityID == 0){
+ supportEntityID = nextId();
+ DataWatcher slimeDatawatcher = DataWatcherUtil.createDataWatcher();
+
+ slimeDatawatcher.a(0, (byte) 0);
+ slimeDatawatcher.a(1, (short) 0);
+ slimeDatawatcher.a(2, "");
+ slimeDatawatcher.a(3, (byte) 0);
+ slimeDatawatcher.a(4, (byte) 0);
+ slimeDatawatcher.a(16, (byte) -2);
+
+ PacketPlayOutSpawnEntityLiving packet = new PacketPlayOutSpawnEntityLiving(
+ supportEntityID,
+ 55,
+ MathHelper.floor(getLocation().getX() * 32.0D),
+ MathHelper.floor(getLocation().getY() * 32.0D),
+ MathHelper.floor(getLocation().getZ() * 32.0D),
+ slimeDatawatcher
+ );
+
+ PacketUtils.sendPacket(player, packet);
+ }
+ PacketUtils.sendPacket(player, new PacketPlayOutAttachEntity(0, supportEntityID, getId()));
+ }else {
+ ScoreboardTeam team = new ScoreboardTeam(((CraftScoreboard) Bukkit.getScoreboardManager().getMainScoreboard()).getHandle(),
+ getDisplayName());
+
+ team.setNameTagVisibility(ScoreboardTeamBase.EnumNameTagVisibility.NEVER);
+
+ PacketUtils.sendPacket(player, new PacketPlayOutScoreboardTeam(team, 1));
+ PacketUtils.sendPacket(player, new PacketPlayOutScoreboardTeam(team, 0));
+ PacketUtils.sendPacket(player, new PacketPlayOutScoreboardTeam(team, new ArrayList(){{add(getDisplayName());}}, 3));
+ }
+ }
+
+ protected void showNamePlate(Player player){
+ if (PlayerUtil.isLegacy(player)){
+ PacketUtils.sendPacket(player, new PacketPlayOutAttachEntity(0, supportEntityID, 0));
+ PacketUtils.sendPacket(player, new PacketPlayOutEntityDestroy(supportEntityID));
+ supportEntityID = 0;
+ }else {
+ ScoreboardTeam team = new ScoreboardTeam(((CraftScoreboard) Bukkit.getScoreboardManager().getMainScoreboard()).getHandle(),
+ getDisplayName());
+
+ team.setNameTagVisibility(ScoreboardTeamBase.EnumNameTagVisibility.ALWAYS);
+
+ PacketUtils.sendPacket(player, new PacketPlayOutScoreboardTeam(team, 1));
+ PacketUtils.sendPacket(player, new PacketPlayOutScoreboardTeam(team, 0));
+ PacketUtils.sendPacket(player, new PacketPlayOutScoreboardTeam(team, new ArrayList(){{add(getDisplayName());}}, 3));
+ }
+ }
+
+ @Override
+ public void sendDestroyPackets(Player player) {
+ PacketUtils.sendPacket(player, new PacketPlayOutEntityDestroy(supportEntityID));
+ super.sendDestroyPackets(player);
+ }
+
+ @Override
+ public void setArmor(Player player) {
+ PlayerInventory inventory = player.getInventory();
+
+ setHelmet(inventory.getHelmet());
+ setChestplate(inventory.getChestplate());
+ setLeggings(inventory.getLeggings());
+ setBoots(inventory.getBoots());
+ }
+
+ @Override
+ public void setArmor(ItemStack[] armor) {
+ setHelmet(armor[3]);
+ setChestplate(armor[2]);
+ setLeggings(armor[1]);
+ setBoots(armor[0]);
+ }
+
+ @Override
+ public void setArmor(ItemStack helmet, ItemStack chestplate, ItemStack leggings, ItemStack boots) {
+ setHelmet(helmet);
+ setChestplate(chestplate);
+ setLeggings(leggings);
+ setBoots(boots);
+ }
+
+ @Override
+ public void setItemInHand(ItemStack itemInHand) {
+ this.itemInHand = itemInHand;
+
+ updateItemInHand(itemInHand);
+ }
+
+ @Override
+ public void setDisplayName(String displayName) {
+ this.displayName = CC.translate(displayName);
+
+ this.gameProfile = new GameProfile(uuid, CC.translate(displayName));
+
+ if (skin != null) {
+ gameProfile.getProperties().put("textures", new Property("textures", skin.getValue(), skin.getSignature()));
+ }
+
+ resendForCurrentWatchers();
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/npc/NPCManager.java b/BattleEntity-main/src/main/java/rip/battle/entity/npc/NPCManager.java
new file mode 100644
index 0000000..d58700e
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/npc/NPCManager.java
@@ -0,0 +1,116 @@
+package rip.battle.entity.npc;
+
+import cc.stormworth.core.util.serialize.ItemStackAdapter;
+import com.google.common.collect.Maps;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import lombok.Getter;
+import org.bukkit.Location;
+import org.bukkit.inventory.ItemStack;
+import rip.battle.entity.EntityPlugin;
+import rip.battle.entity.hologram.api.Hologram;
+import rip.battle.entity.hologram.serialization.HologramTypeAdapter;
+import rip.battle.entity.npc.api.NPC;
+import rip.battle.entity.npc.serialization.NPCTypeAdapter;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Map;
+
+@Getter
+public class NPCManager {
+
+ private final Map npcs = Maps.newHashMap();
+
+ private final EntityPlugin plugin;
+
+ public NPCManager(EntityPlugin plugin) {
+ this.plugin = plugin;
+ loadNPCs();
+ }
+
+ public void loadNPCs() {
+ File file = new File(plugin.getDataFolder(), "npcs");
+
+ if (!file.exists()){
+ return;
+ }
+
+ Arrays.stream(file.listFiles()).forEach(file1 -> {
+
+ FileReader fileReader;
+
+ try {
+ fileReader = new FileReader(file1);
+
+ JsonElement jsonElement = new Gson().fromJson(fileReader, JsonElement.class);
+ NPC npc = NPCTypeAdapter.fromJson(jsonElement);
+
+ npcs.put(file1.getName().replace(".json", ""), npc);
+
+ System.out.println("Loaded NPC: " + npc.getName());
+
+ fileReader.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ });
+ }
+
+ public void saveNPCs(){
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(ItemStack.class, new ItemStackAdapter())
+ .registerTypeAdapter(Hologram.class, new HologramTypeAdapter())
+ .enableComplexMapKeySerialization()
+ .setPrettyPrinting()
+ .create();
+
+ File file = new File(plugin.getDataFolder(), "npcs");
+
+ if (!file.exists())
+ file.mkdirs();
+
+ npcs.forEach((name, hologram) -> {
+ try {
+ FileWriter writer = new FileWriter(new File(plugin.getDataFolder(),
+ File.separator + "npcs" + File.separator + name + ".json"));
+
+ writer.write(gson.toJson(NPCTypeAdapter.toJson(hologram)));
+ System.out.println("Saved NPC: " + name);
+
+ writer.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ });
+ }
+
+ public void addNPC(NPC npc) {
+ npcs.put(npc.getName(), npc);
+ }
+
+ public void removeNPC(NPC npc) {
+ npc.destroy();
+ npcs.remove(npc.getName());
+
+ File file = new File(plugin.getDataFolder(), "npcs" + File.separator + npc.getName() + ".json");
+
+ if (file.exists())
+ file.delete();
+ }
+
+ public NPC getNPC(String name) {
+ return npcs.get(name);
+ }
+
+ public void createNPC(String name, Location location) {
+ NPC npc = new NPCEntity(name, location);
+
+ npc.spawn();
+ addNPC(npc);
+ }
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/npc/api/NPC.java b/BattleEntity-main/src/main/java/rip/battle/entity/npc/api/NPC.java
new file mode 100644
index 0000000..ca45f9f
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/npc/api/NPC.java
@@ -0,0 +1,90 @@
+package rip.battle.entity.npc.api;
+
+import cc.stormworth.core.util.skin.SkinTexture;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import rip.battle.entity.hologram.api.Hologram;
+
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+
+public interface NPC {
+
+ String getName();
+
+ void setSkin(SkinTexture skin);
+
+ SkinTexture getSkin();
+
+ void destroy();
+
+ void destroy(Player player);
+
+ void spawn();
+
+ void spawn(Player player);
+
+ void updateVisibility(boolean hidden);
+
+ void updateVisibilityFor(Player player, boolean hidden);
+
+ boolean isVisible();
+
+ boolean isVisibleTo(Player player);
+
+ void teleport(double x, double y, double z);
+
+ void teleport(double x, double y, double z, float yaw, float pitch);
+
+ void teleport(Location location);
+
+ void teleport(Location location, float yaw, float pitch);
+
+ Location getLocation();
+
+ ItemStack getHelmet();
+
+ ItemStack getChestplate();
+
+ ItemStack getLeggings();
+
+ ItemStack getBoots();
+
+ ItemStack getItemInHand();
+
+ void setHelmet(ItemStack itemStack);
+
+ void setChestplate(ItemStack itemStack);
+
+ void setLeggings(ItemStack itemStack);
+
+ void setBoots(ItemStack itemStack);
+
+ void setItemInHand(ItemStack itemStack);
+
+ void setArmor(Player player);
+
+ void setArmor(ItemStack[] armor);
+
+ void setArmor(ItemStack helmet, ItemStack chestplate, ItemStack leggings, ItemStack boots);
+
+ void setDisplayName(String displayName);
+
+ String getDisplayName();
+
+ Hologram getHologram();
+
+ void setHologram(Hologram hologram);
+
+ void setMessages(List messages);
+
+ List getMessages();
+
+ Set getCommands();
+
+ void setOnRightClick(Consumer onRightClick);
+
+ void setOnLeftClick(Consumer onLeftClick);
+}
diff --git a/BattleEntity-main/src/main/java/rip/battle/entity/npc/commands/NPCCommand.java b/BattleEntity-main/src/main/java/rip/battle/entity/npc/commands/NPCCommand.java
new file mode 100644
index 0000000..e9a92a7
--- /dev/null
+++ b/BattleEntity-main/src/main/java/rip/battle/entity/npc/commands/NPCCommand.java
@@ -0,0 +1,33 @@
+package rip.battle.entity.npc.commands;
+
+import cc.stormworth.core.util.chat.CC;
+import com.jonahseguin.drink.annotation.Command;
+import com.jonahseguin.drink.annotation.Sender;
+import org.bukkit.entity.Player;
+
+public class NPCCommand {
+
+ @Command(name = "", desc = "Allows you to manage your npc", usage = "")
+ public void npc(@Sender Player player) {
+ player.sendMessage(CC.translate("&e&lNPC Commands"));
+
+ player.sendMessage(CC.translate("&e/npc create &7- &fCreates a npc with the given name."));
+ player.sendMessage(CC.translate("&e/npc remove &7- &fRemoves a npc with the given name."));
+ player.sendMessage(CC.translate("&e/npc setline [text] &7- &fSets a line on a npc."));
+ player.sendMessage(CC.translate("&e/npc removeline &7- &fRemoves a line from a npc."));
+ player.sendMessage(CC.translate("&e/npc addline &7- &fAdds a line to a npc."));
+ player.sendMessage(CC.translate("&e/npc additemline - &7- &fAdds a line with an item to a npc."));
+ player.sendMessage(CC.translate("&e/npc teleport &7- &fTeleports you to a npc."));
+ player.sendMessage(CC.translate("&e/npc movehere &7- &fSets the location of a npc."));
+ player.sendMessage(CC.translate("&e/npc setdisplayname