diff --git a/src/main/java/com/night/nullvalkyrie/Events/CustomItemEvents.java b/src/main/java/com/night/nullvalkyrie/Events/CustomItemEvents.java index e770033..0753103 100644 --- a/src/main/java/com/night/nullvalkyrie/Events/CustomItemEvents.java +++ b/src/main/java/com/night/nullvalkyrie/Events/CustomItemEvents.java @@ -27,7 +27,7 @@ public class CustomItemEvents implements Listener { if (pl.getInventory().getItemInMainHand().getItemMeta() != null) { String name = pl.getInventory().getItemInMainHand().getItemMeta().getDisplayName(); if (name.equalsIgnoreCase(Rarity.ULTRA.getColor() + "Snow Gun")) { - e.setDamage(10000); + e.setDamage(2000); } else if (name.equalsIgnoreCase("AA-12")) { e.setDamage(7); } else { diff --git a/src/main/java/com/night/nullvalkyrie/Items/CustomItemManager.java b/src/main/java/com/night/nullvalkyrie/Items/CustomItemManager.java index e202a63..ec00b70 100644 --- a/src/main/java/com/night/nullvalkyrie/Items/CustomItemManager.java +++ b/src/main/java/com/night/nullvalkyrie/Items/CustomItemManager.java @@ -1,19 +1,18 @@ package com.night.nullvalkyrie.Items; import com.night.nullvalkyrie.Main; -import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeModifier; +import org.bukkit.configuration.MemorySection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.ShapedRecipe; import org.bukkit.inventory.meta.ItemMeta; import java.io.File; @@ -30,6 +29,7 @@ public class CustomItemManager { main.getDataFolder().mkdir(); } createItemDataDirectory("ItemData"); + createFilesFromConfig(main.getConfig()); register(); } public void register() { @@ -175,6 +175,30 @@ public class CustomItemManager { } return ns; } + public void createFilesFromConfig(FileConfiguration config) { + for(String a : config.getKeys(false)) { + FileConfiguration c = loadConfig("ItemData\\" + a + ".yml"); + for(String b : config.getKeys(true)) { + if(b.startsWith(a)) { + List d = new ArrayList<>(Arrays.asList(b.split("\\."))); + if(d.size() != 1) { + d.remove(0); + if(d.size() == 1) { + c.set(d.get(0), config.get(b)); + } else { + c.set(String.join(".", d), config.get(b)); + } + try { + c.save(loadFile("ItemData\\" + a + ".yml")); + } catch (IOException e) { + e.printStackTrace(); + } + } + + } + } + } + } public static ItemStack getItem(String name){ return a.get(name); } diff --git a/src/main/java/com/night/nullvalkyrie/Main.java b/src/main/java/com/night/nullvalkyrie/Main.java index 8648821..f92b1c5 100644 --- a/src/main/java/com/night/nullvalkyrie/Main.java +++ b/src/main/java/com/night/nullvalkyrie/Main.java @@ -50,6 +50,8 @@ public final class Main extends JavaPlugin implements Listener { @Override public void onEnable() { + getConfig().options().copyDefaults(); + saveDefaultConfig(); new VanishCommand(); new TestCommand(); new AnvilCommand(); diff --git a/src/main/java/com/night/nullvalkyrie/Util/Utils.java b/src/main/java/com/night/nullvalkyrie/Util/Utils.java new file mode 100644 index 0000000..ea54905 --- /dev/null +++ b/src/main/java/com/night/nullvalkyrie/Util/Utils.java @@ -0,0 +1,49 @@ +package com.night.nullvalkyrie.Util; + +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.ArrayList; +import java.util.List; + +public class Utils { + public static String color(String string) { + return ChatColor.translateAlternateColorCodes('&', string); + } + + public static ItemStack createItem(Material type, int amount, String name, String... lines) { + ItemStack item = new ItemStack(type, amount); + ItemMeta meta = item.getItemMeta(); + meta.setUnbreakable(true); + meta.addItemFlags(ItemFlag.HIDE_UNBREAKABLE, ItemFlag.HIDE_ENCHANTS); + if (name != null) meta.setDisplayName(color(name)); + if (lines != null) { + List lore = new ArrayList<>(); + for (String line : lines) { + lore.add(color(line)); + } + meta.setLore(lore); + } + item.setItemMeta(meta); + return item; + } + + public static ItemStack enchantItem(ItemStack item, Enchantment enchant, int level) { + item.addUnsafeEnchantment(enchant, level); + return item; + } + + public static ItemStack[] makeArmorSet(ItemStack helmet, ItemStack chestplate, ItemStack leggings, ItemStack boots) { + ItemStack[] armor = new ItemStack[4]; + armor[3] = helmet; + armor[2] = chestplate; + armor[1] = leggings; + armor[0] = boots; + return armor; + } + +} diff --git a/src/main/java/com/night/nullvalkyrie/Util/components/CustomMob.java b/src/main/java/com/night/nullvalkyrie/Util/components/CustomMob.java new file mode 100644 index 0000000..863e77c --- /dev/null +++ b/src/main/java/com/night/nullvalkyrie/Util/components/CustomMob.java @@ -0,0 +1,111 @@ +package com.night.nullvalkyrie.Util.components; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.attribute.Attribute; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; + +import java.util.Arrays; +import java.util.List; + +import static com.night.nullvalkyrie.Util.Utils.color; +import static com.night.nullvalkyrie.Util.Utils.*; + +public enum CustomMob { + + DESERT_RISEN( + "&6Desert Risen", + 2048, + 60, + EntityType.HUSK, + null, + null, + new LootItem(createItem(Material.ROTTEN_FLESH, 1, "&fPreserved Flesh", "&7A preserved flesh from a rotting corpse", "&7Not sure what you'd want this for, though", "&7", "&9Foodstuff"), 1, 3, 100)), + + SKELETAL_MAGE( + "&dSkeletal Mage", + 2048, + 15, + EntityType.SKELETON, + createItem(Material.BONE, 1, null), makeArmorSet(new ItemStack(Material.IRON_HELMET), + null, + null, + null), + new LootItem(createItem(Material.BONE, 1, "&dBone Wand", "&7A wand made from skeletal bones"), 30), + new LootItem(new ItemStack(Material.BONE), 1, 3, 100)), + + ZOMBIE_SQUIRE( + "&bZombie Squire", + 2048, + 12, + EntityType.ZOMBIE, + new ItemStack(Material.IRON_SWORD), + makeArmorSet(new ItemStack(Material.CHAINMAIL_HELMET), new ItemStack(Material.CHAINMAIL_CHESTPLATE), new ItemStack(Material.CHAINMAIL_LEGGINGS), new ItemStack(Material.IRON_BOOTS)), new LootItem(new ItemStack(Material.CHAINMAIL_CHESTPLATE), 35), new LootItem(new ItemStack(Material.CHAINMAIL_LEGGINGS), 35), new LootItem(new ItemStack(Material.CHAINMAIL_HELMET), 35), new LootItem(new ItemStack(Material.IRON_BOOTS), 25), new LootItem(new ItemStack(Material.IRON_SWORD), 40)), + + CHARRED_ARCHER( + "&8Charred Archer", + 2048, + 3, + EntityType.WITHER_SKELETON, + enchantItem(new ItemStack(Material.BOW), Enchantment.ARROW_KNOCKBACK, 2), + null, new LootItem(enchantItem(enchantItem(createItem(Material.BOW, 1, "&cBurnt Bow", "&7This bow is burnt to a crisp but remains intact", "&8due to special enchantments"), Enchantment.ARROW_FIRE, 1), Enchantment.ARROW_KNOCKBACK, 2), 100), + new LootItem(new ItemStack(Material.BONE), 1, 5, 100)), + ; + + private String name; + private double maxHealth, spawnChance; + private EntityType type; + private ItemStack mainItem; + private ItemStack[] armor; + private List lootTable; + + CustomMob(String name, double maxHealth, double spawnChance, EntityType type, ItemStack mainItem, ItemStack[] armor, LootItem... lootItems) { + this.name = name; + this.maxHealth = maxHealth; + this.spawnChance = spawnChance; + this.type = type; + this.mainItem = mainItem; + this.armor = armor; + lootTable = Arrays.asList(lootItems); + } + + public LivingEntity spawn(Location location) { + LivingEntity entity = (LivingEntity) location.getWorld().spawnEntity(location, type); + entity.setCustomNameVisible(true); + entity.setCustomName(color(name + " &r&c" + (int) maxHealth + "/" + (int) maxHealth + "❤")); + entity.getAttribute(Attribute.GENERIC_MAX_HEALTH).setBaseValue(maxHealth); + entity.setHealth(maxHealth); + EntityEquipment inv = entity.getEquipment(); + if (armor != null) inv.setArmorContents(armor); + inv.setHelmetDropChance(0f); + inv.setChestplateDropChance(0f); + inv.setLeggingsDropChance(0f); + inv.setBootsDropChance(0f); + inv.setItemInMainHand(mainItem); + inv.setItemInMainHandDropChance(0f); + return entity; + } + + public void tryDropLoot(Location location) { + for (LootItem item : lootTable) { + item.tryDropItem(location); + } + } + + public String getName() { + return name; + } + + public double getMaxHealth() { + return maxHealth; + } + + public double getSpawnChance() { + return spawnChance; + } + +} diff --git a/src/main/java/com/night/nullvalkyrie/Util/components/LootItem.java b/src/main/java/com/night/nullvalkyrie/Util/components/LootItem.java new file mode 100644 index 0000000..bbb79d5 --- /dev/null +++ b/src/main/java/com/night/nullvalkyrie/Util/components/LootItem.java @@ -0,0 +1,36 @@ +package com.night.nullvalkyrie.Util.components; + +import org.bukkit.Location; +import org.bukkit.inventory.ItemStack; + +import java.util.Random; + +public class LootItem { + + private ItemStack item; + private int min = 1, max = 1; + private double dropRate; + private static Random randomiser = new Random(); + + public LootItem(ItemStack item, double dropRate) { + this.item = item; + this.dropRate = dropRate; + } + + public LootItem(ItemStack item, int min, int max, double dropRate) { + this.item = item; + this.min = min; + this.max = max; + this.dropRate = dropRate; + } + + public void tryDropItem(Location loc) { + if (Math.random() * 101 > dropRate) return; + int amount = randomiser.nextInt(max - min + 1) + min; + if (amount == 0) return; + ItemStack item = this.item.clone(); + item.setAmount(amount); + loc.getWorld().dropItemNaturally(loc, item); + } + +} diff --git a/src/main/java/com/night/nullvalkyrie/commands/SpawnCommand.java b/src/main/java/com/night/nullvalkyrie/commands/SpawnCommand.java index ae7cd74..e65b871 100644 --- a/src/main/java/com/night/nullvalkyrie/commands/SpawnCommand.java +++ b/src/main/java/com/night/nullvalkyrie/commands/SpawnCommand.java @@ -1,33 +1,151 @@ package com.night.nullvalkyrie.commands; +import com.night.nullvalkyrie.Main; +import com.night.nullvalkyrie.Util.components.CustomMob; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; import org.bukkit.command.CommandSender; import org.bukkit.entity.*; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.scheduler.BukkitRunnable; -import java.util.List; +import java.text.DecimalFormat; +import java.util.*; -public class SpawnCommand extends Command { - public SpawnCommand() { +import static com.night.nullvalkyrie.Util.Utils.color; + +public class SpawnCommand extends Command implements Listener { + private final Main main; + public World world; + public final Map indicators = new HashMap<>(); + public final Map entities = new HashMap<>(); + private final DecimalFormat formatter = new DecimalFormat("#"); + public SpawnCommand(Main main) { super( "spawn", new String[]{}, "Spawn a custom mob", "" ); + this.main = main; } @Override public void onCommand(CommandSender sender, String[] args) { - Player player = (Player) sender; - Entity ent = player.getWorld().spawnEntity((player.getLocation().add(0, 2, 0)), EntityType.ZOMBIE); - ent.setCustomName("Gay"); - ent.setCustomNameVisible(true); - double ourHealth = 20 * 5; - Damageable ente = (Damageable) ent; - ente.setMaxHealth(ourHealth); - ente.setHealth(ourHealth); + world = Bukkit.getWorld("world"); + + spawnMobs(9, 10, 5 * 20); + new BukkitRunnable() { + final Set stands = indicators.keySet(); + final List removal = new ArrayList<>(); + @Override + public void run() { + for (Entity stand : stands) { + int ticksLeft = indicators.get(stand); + if (ticksLeft == 0) { + stand.remove(); + removal.add(stand); + continue; + } + ticksLeft--; + indicators.put(stand, ticksLeft); + } + stands.removeAll(removal); + } + }.runTaskTimer(main, 0L, 1L); } + public void spawnMobs(int size, int mobCap, int spawnTime) { + CustomMob[] mobTypes = CustomMob.values(); + new BukkitRunnable() { + final Set spawned = entities.keySet(); + final List removal = new ArrayList<>(); + @Override + public void run() { + for (Entity entity : spawned) { + if (!entity.isValid() || entity.isDead()) removal.add(entity); + } + spawned.removeAll(removal); + // Spawning Algorithm + int diff = mobCap - entities.size(); + if (diff <= 0) return; + int spawnAmount = (int) (Math.random() * (diff + 1)), count = 0; + while (count <= spawnAmount) { + count++; + int ranX = getRandomWithNeg(size), ranZ = getRandomWithNeg(size); + Block block = world.getHighestBlockAt(ranX, ranZ); + double xOffset = getRandomOffset(), zOffset = getRandomOffset(); + Location loc = block.getLocation().clone().add(xOffset, 1, zOffset); + if (!isSpawnable(loc)) continue; + double random = Math.random() * 101, previous = 0; + CustomMob typeToSpawn = mobTypes[0]; + for (CustomMob type : mobTypes) { + previous += type.getSpawnChance(); + if (random <= previous) { + typeToSpawn = type; + break; + } + } + entities.put(typeToSpawn.spawn(loc), typeToSpawn); + } + } + }.runTaskTimer(main, 0L, spawnTime); + } + + private static boolean isSpawnable(Location loc) { + Block feetBlock = loc.getBlock(), headBlock = loc.clone().add(0, 1, 0).getBlock(), upperBlock = loc.clone().add(0, 2, 0).getBlock(); + return feetBlock.isPassable() && !feetBlock.isLiquid() && headBlock.isPassable() && !headBlock.isLiquid() && upperBlock.isPassable() && !upperBlock.isLiquid(); + } + + private static double getRandomOffset() { + double random = Math.random(); + if (Math.random() > 0.5) random *= -1; + return random; + } + + private static int getRandomWithNeg(int size) { + int random = (int) (Math.random() * (size + 1)); + if (Math.random() > 0.5) random *= -1; + return random; + } + + @EventHandler + public void onEntityDamage(EntityDamageEvent event) { + Entity rawEntity = event.getEntity(); + if (!entities.containsKey(rawEntity)) return; + CustomMob mob = entities.get(rawEntity); + LivingEntity entity = (LivingEntity) rawEntity; + double damage = event.getFinalDamage(), health = entity.getHealth() + entity.getAbsorptionAmount(); + if (health > damage) { + // If the entity survived the hit + health -= damage; + entity.setCustomName(color(mob.getName() + " &r&c" + (int) health + "/" + (int) mob.getMaxHealth() + "❤")); + } + Location loc = entity.getLocation().clone().add(getRandomOffset(), 1, getRandomOffset()); + world.spawn(loc, ArmorStand.class, armorStand -> { + armorStand.setMarker(true); + armorStand.setVisible(false); + armorStand.setGravity(false); + armorStand.setSmall(true); + armorStand.setCustomNameVisible(true); + armorStand.setCustomName(color("&c" + formatter.format(damage))); + indicators.put(armorStand, 30); + }); + } + + @EventHandler + public void onEntityDeath(EntityDeathEvent event) { + if (!entities.containsKey(event.getEntity())) return; + event.setDroppedExp(0); + event.getDrops().clear(); + entities.remove(event.getEntity()).tryDropLoot(event.getEntity().getLocation()); + } @Override public List onTabComplete(CommandSender sender, String[] args) { return null; diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..503a063 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,128 @@ +ExplosiveBow: + name: "Explosive Bow" + material: "BOW" + type: "Weapon" + rarity: LEGENDARY + lore: + properties: + damage: 50 + ability: + name: Explosive Shot + details: + - Shoot a explosive arrow that causes lots of damage + - Arrow won't destroy blocks + enchants: + thunderbolt: 5 + attributes: + damage: 50.0 +FragGrenade: + name: "Frag Grenade" + material: "EGG" + type: "Weapon" + rarity: LEGENDARY + lore: + properties: + damage: 50 + ability: + name: TNT Party + details: + - Throw a *large* TNT explode with tons of damage + enchants: + thunderbolt: 5 + attributes: + damage: 50.0 +GrapplingHook: + name: "Grappling Hook" + material: "FISHING_ROD" + type: "Weapon" + rarity: RARE + lore: + ability: + name: Hook + details: + - Using it will make you fly + enchants: + thunderbolt: 5 +SnowGun: + name: "Snow Gun" + material: "DIAMOND_HOE" + type: "Weapon" + rarity: ULTRA + lore: + properties: + damage: 25 + speed: 20 + ability: + name: Let it go + details: + - Shoot Snowball that cause lots of damage + enchants: + thunderbolt: 5 + attributes: + damage: 25.0 + moveSpeed: 0.2 +TeleportDoor: + name: "Teleport Door" + material: "DIAMOND_SHOVEL" + type: "Weapon" + rarity: GRAND + lore: + properties: + damage: 75 + speed: 20 + ability: + name: Instant Teleport + details: + - Teleport to 12 blocks away from you + enchants: + thunderbolt: 5 + attributes: + damage: 75.0 + moveSpeed: 0.2 +Terminator: + name: "Terminator" + material: "BOW" + type: "Weapon" + rarity: MYTHIC + lore: + properties: + damage: 50 + speed: 10 + ability: + name: Triple Shot + details: + - Shoot three arrow at one time + - Arrow deals 50% more damage + enchants: + sharpness: 20 + looting: 10 + thunderbolt: 5 + attributes: + damage: 50.0 + moveSpeed: 0.1 +WidowSword: + name: "Widow Sword" + material: "STICK" + type: "Weapon" + rarity: MYTHIC + lore: + properties: + damage: 100 + speed: 20 + ability: + name: Damage Multiplier + details: + - Damage dealt to mobs will be multiplied + - Zombie + 100% + - Skeleton + 100% + - Spider + 100% + enchants: + sharpness: 20 + looting: 10 + thunderbolt: 5 + attributes: + damage: 100.0 + moveSpeed: 0.2 + zombie: 100 + skeleton: 100 + spider: 100