diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..3142cba
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,50 @@
+
+ 4.0.0
+
+ com.zivilon
+ DungeonTools
+ 1.4
+ jar
+
+ DungeonTools
+
+
+ UTF-8
+ 1.7
+ 1.7
+
+
+
+ org.bukkit
+ bukkit
+ 1.7.10-R0.1-SNAPSHOT
+ provided
+
+
+ com.github.flinbein
+ PowerNBT
+ 0.8.9.2
+
+
+
+
+ package
+
+
+ src/main/resources
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.0
+
+ 1.7
+ 1.7
+
+
+
+
+
diff --git a/src/main/java/com/zivilon/dungeontools/Area.java b/src/main/java/com/zivilon/dungeontools/Area.java
new file mode 100644
index 0000000..7e77754
--- /dev/null
+++ b/src/main/java/com/zivilon/dungeontools/Area.java
@@ -0,0 +1,67 @@
+package com.zivilon.dungeontools;
+
+import org.bukkit.Location;
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.LivingEntity;
+
+import java.util.List;
+import java.util.Map;
+
+import com.zivilon.dungeontools.TrackedEntity;
+
+public class Area {
+ public final List tracked_entities;
+ public final Location corner_1;
+ public final Location corner_2;
+ public final Map commands;
+ public int count;
+ public final String playerDeathCommand;
+
+ public Area(List tracked_entities, Location corner_1, Location corner_2, Map commands, String playerDeathCommand) {
+ this.tracked_entities = tracked_entities;
+ this.corner_1 = corner_1;
+ this.corner_2 = corner_2;
+ this.commands = commands;
+ this.count = 0;
+ this.playerDeathCommand = playerDeathCommand;
+ }
+
+ public boolean contains(Location location) {
+ double minX = Math.min(corner_1.getX(), corner_2.getX());
+ double minY = Math.min(corner_1.getY(), corner_2.getY());
+ double minZ = Math.min(corner_1.getZ(), corner_2.getZ());
+ double maxX = Math.max(corner_1.getX(), corner_2.getX());
+ double maxY = Math.max(corner_1.getY(), corner_2.getY());
+ double maxZ = Math.max(corner_1.getZ(), corner_2.getZ());
+
+ return location.getX() >= minX && location.getX() <= maxX &&
+ location.getY() >= minY && location.getY() <= maxY &&
+ location.getZ() >= minZ && location.getZ() <= maxZ;
+ }
+
+
+ public boolean containsEntity(LivingEntity entity) {
+ for (TrackedEntity tracked : tracked_entities) {
+ if (tracked.matches(entity)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void incrementCount() {
+ count++;
+ }
+
+ public int get_count() {
+ return count;
+ }
+
+ public String getCommandForCount() {
+ return commands.get(count);
+ }
+
+ public String getPlayerDeathCommand() {
+ return playerDeathCommand;
+ }
+}
diff --git a/src/main/java/com/zivilon/dungeontools/DungeonTools.java b/src/main/java/com/zivilon/dungeontools/DungeonTools.java
new file mode 100644
index 0000000..e8eccd6
--- /dev/null
+++ b/src/main/java/com/zivilon/dungeontools/DungeonTools.java
@@ -0,0 +1,88 @@
+package com.zivilon.dungeontools;
+
+import org.bukkit.plugin.java.JavaPlugin;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.configuration.file.YamlConfiguration;
+
+import java.io.File;
+import java.util.HashMap;
+
+import com.zivilon.dungeontools.Area;
+import com.zivilon.dungeontools.listeners.kill_tracker_listener;
+import com.zivilon.dungeontools.listeners.barrow_wight_teleport_listener;
+import com.zivilon.dungeontools.listeners.aggression_listener;
+import com.zivilon.dungeontools.commands.kill_tracker_command;
+import com.zivilon.dungeontools.commands.loot_chest_command;
+import com.zivilon.dungeontools.commands.playsound_command;
+
+public class DungeonTools extends JavaPlugin {
+ public FileConfiguration loot_chest_config;
+ public FileConfiguration items_config;
+ public FileConfiguration areas_config;
+
+ public boolean enable_specific_listeners = false;
+
+ public HashMap tracking_areas = new HashMap<>();
+
+ @Override
+ public void onEnable() {
+ save_default_items_config();
+
+ this.loot_chest_config = load_config_file("loot_chests.yml");
+ this.items_config = load_config_file("items.yml");
+ this.areas_config = load_config_file("areas.yml");
+
+
+ // Fired when the server enables the plugin
+ getLogger().info("DungeonTools v1.4 enabled!");
+ // Register the /loot_chest command
+ this.getCommand("loot_chest").setExecutor(new loot_chest_command(this));
+ // Register the /track_kills command
+ this.getCommand("track_kills").setExecutor(new kill_tracker_command(this, tracking_areas));
+ // Register the /playsound_loc command
+ getCommand("playsound_loc").setExecutor(new playsound_command());
+ // Register the EntityDeath event listener
+ getServer().getPluginManager().registerEvents(new kill_tracker_listener(tracking_areas), this);
+ // Register the EntityDamage event listener for aggression control
+ getServer().getPluginManager().registerEvents(new aggression_listener(this), this);
+
+ if (enable_specific_listeners) {
+ // Register the EntityDamage event listener
+ getServer().getPluginManager().registerEvents(new barrow_wight_teleport_listener(), this);
+ }
+ }
+
+ @Override
+ public void onDisable() {
+ getLogger().info("DungeonTools disabled!");
+ }
+
+ public FileConfiguration get_loot_chest_config() {
+ return this.loot_chest_config;
+ }
+
+ public FileConfiguration get_items_config() {
+ return items_config;
+ }
+
+ public FileConfiguration get_areas_config() {
+ return areas_config;
+ }
+
+ public FileConfiguration load_config_file(String file_name) {
+ File file = new File(getDataFolder(), file_name);
+ if (!file.exists()) {
+ saveResource(file_name, false);
+ }
+ return YamlConfiguration.loadConfiguration(file);
+ }
+
+ public void save_default_items_config() {
+ File items_file = new File(getDataFolder(), "items.yml");
+ if (!items_file.exists()) {
+ saveResource("items.yml", false);
+ }
+ items_config = YamlConfiguration.loadConfiguration(items_file);
+ }
+
+}
diff --git a/src/main/java/com/zivilon/dungeontools/TrackedEntity.java b/src/main/java/com/zivilon/dungeontools/TrackedEntity.java
new file mode 100644
index 0000000..affa27d
--- /dev/null
+++ b/src/main/java/com/zivilon/dungeontools/TrackedEntity.java
@@ -0,0 +1,32 @@
+package com.zivilon.dungeontools;
+
+import org.bukkit.ChatColor;
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.LivingEntity;
+
+public class TrackedEntity {
+ public final EntityType type;
+ public final String name;
+
+ public TrackedEntity(EntityType type, String name) {
+ this.type = type;
+ this.name = name;
+ }
+
+ public boolean matches(LivingEntity entity) {
+ if (entity.getType() != type) {
+ return false;
+ }
+ if (name == null) {
+ return true;
+ }
+ return name.equals(entity.getCustomName());
+ }
+
+ public static TrackedEntity parseTrackedEntity(String s) {
+ String[] split = s.split(":", 2);
+ EntityType type = EntityType.valueOf(split[0]);
+ String name = split.length > 1 ? ChatColor.translateAlternateColorCodes('&', split[1]) : null;
+ return new TrackedEntity(type, name);
+ }
+}
diff --git a/src/main/java/com/zivilon/dungeontools/commands/kill_tracker_command.java b/src/main/java/com/zivilon/dungeontools/commands/kill_tracker_command.java
new file mode 100644
index 0000000..7a21224
--- /dev/null
+++ b/src/main/java/com/zivilon/dungeontools/commands/kill_tracker_command.java
@@ -0,0 +1,90 @@
+package com.zivilon.dungeontools.commands;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.ChatColor;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.entity.EntityType;
+import org.bukkit.plugin.java.JavaPlugin;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.zivilon.dungeontools.Area;
+import com.zivilon.dungeontools.TrackedEntity;
+import com.zivilon.dungeontools.DungeonTools;
+
+public class kill_tracker_command implements CommandExecutor {
+ private final DungeonTools plugin;
+ private Map tracking_areas;
+
+ public kill_tracker_command(DungeonTools plugin, Map tracking_areas) {
+ this.plugin = plugin;
+ this.tracking_areas = tracking_areas;
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if (!sender.hasPermission("dungeontools.kill_tracker")) {
+ sender.sendMessage(ChatColor.RED + "You do not have permission to use this command.");
+ return true;
+ }
+
+ if (args.length < 2) {
+ return false;
+ }
+
+ String area_name = args[1];
+ ConfigurationSection section = plugin.get_areas_config().getConfigurationSection("areas." + area_name);
+
+ if (args[0].equalsIgnoreCase("start")) {
+ if (section == null) {
+ sender.sendMessage("No such area: " + area_name);
+ return true;
+ }
+
+ start_tracking(section);
+ sender.sendMessage("Started tracking kills in " + area_name);
+
+ } else if (args[0].equalsIgnoreCase("cancel")) {
+ tracking_areas.remove(area_name);
+ sender.sendMessage("Cancelled tracking kills in " + area_name);
+ }
+
+ return true;
+ }
+
+ private void start_tracking(ConfigurationSection section) {
+ String world_name = section.getString("world");
+ World world = Bukkit.getWorld(world_name);
+
+ List entity_names = section.getStringList("tracked_entities");
+ List tracked_entities = new ArrayList<>();
+ for (String name : entity_names) {
+ tracked_entities.add(TrackedEntity.parseTrackedEntity(name));
+ }
+
+ ConfigurationSection commands_section = section.getConfigurationSection("killcounts");
+ Map commands = new HashMap<>();
+ for (String key : commands_section.getKeys(false)) {
+ commands.put(Integer.parseInt(key), commands_section.getString(key));
+ }
+
+ String[] corner_1 = section.getString("corner_1").split(",");
+ String[] corner_2 = section.getString("corner_2").split(",");
+ Location corner_1_location = new Location(world, Double.parseDouble(corner_1[0]), Double.parseDouble(corner_1[1]), Double.parseDouble(corner_1[2]));
+ Location corner_2_location = new Location(world, Double.parseDouble(corner_2[0]), Double.parseDouble(corner_2[1]), Double.parseDouble(corner_2[2]));
+
+ String playerDeathCommand = section.getString("player_death_command", null);
+
+ Area area = new Area(tracked_entities, corner_1_location, corner_2_location, commands, playerDeathCommand);
+
+ tracking_areas.put(section.getName(), area);
+ }
+}
diff --git a/src/main/java/com/zivilon/dungeontools/commands/loot_chest_command.java b/src/main/java/com/zivilon/dungeontools/commands/loot_chest_command.java
new file mode 100644
index 0000000..a415ab9
--- /dev/null
+++ b/src/main/java/com/zivilon/dungeontools/commands/loot_chest_command.java
@@ -0,0 +1,327 @@
+package com.zivilon.dungeontools.commands;
+
+import me.dpohvar.powernbt.PowerNBT;
+import me.dpohvar.powernbt.api.NBTCompound;
+import me.dpohvar.powernbt.api.NBTManager;
+import me.dpohvar.powernbt.api.NBTList;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.ChatColor;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.block.Chest;
+import org.bukkit.block.BlockFace;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.Inventory;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.concurrent.ThreadLocalRandom;
+
+import com.zivilon.dungeontools.DungeonTools;
+import com.zivilon.dungeontools.utilities.enchantments;
+
+public class loot_chest_command implements CommandExecutor {
+ private final DungeonTools plugin;
+
+ public loot_chest_command(DungeonTools plugin) {
+ this.plugin = plugin;
+ }
+
+ NBTManager NBT_manager = me.dpohvar.powernbt.PowerNBT.getApi();
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if (!sender.hasPermission("dungeontools.loot_chest")) {
+ sender.sendMessage(ChatColor.RED + "You do not have permission to use this command.");
+ return true;
+ }
+
+ if (args.length < 5) {
+ sender.sendMessage(ChatColor.RED + "Usage: /loot_chest [-d -s] ");
+ return false;
+ }
+
+ boolean debug_flag = false;
+ boolean silent_flag = false;
+ int argument_offset = 0; // this offset helps us handle the arguments correctly
+
+ // Iterate over flags
+ for (; argument_offset < args.length; argument_offset++) {
+ if (args[argument_offset].startsWith("-")) {
+ debug_flag |= args[argument_offset].contains("d");
+ silent_flag |= args[argument_offset].contains("s");
+ } else {
+ break;
+ }
+ }
+
+ if (args.length - argument_offset != 6) {
+ if (debug_flag) sender.sendMessage("Failed at args.length - argument_offset != 6 with args.length " + args.length + " and argument_offset " + argument_offset);
+ sender.sendMessage(ChatColor.RED + "Usage: /loot_chest [-d -s] ");
+ return false;
+ }
+
+ String direction = args[5 + argument_offset].toUpperCase();
+ int x = Integer.parseInt(args[1 + argument_offset]);
+ int y = Integer.parseInt(args[2 + argument_offset]);
+ int z = Integer.parseInt(args[3 + argument_offset]);
+ String world_name = args[0 + argument_offset];
+ String chest_name = args[4 + argument_offset];
+
+ // Get the world
+ World world = Bukkit.getWorld(world_name);
+ if (world == null) {
+ sender.sendMessage(ChatColor.RED + "World " + world_name + " not found.");
+ return true;
+ }
+
+
+ FileConfiguration config = plugin.getConfig();
+ if (debug_flag) { System.out.println("[DEBUG] config: " + config); } // Debug print statement
+ ConfigurationSection chest_section = config.getConfigurationSection("chests." + chest_name);
+ if (debug_flag) { System.out.println("[DEBUG] chest_section: " + chest_section); } // Debug print statement
+
+ if (chest_section == null) {
+ sender.sendMessage(ChatColor.RED + "Chest " + chest_name + " not found in configuration.");
+ return true;
+ }
+
+ NBTList items = new NBTList();
+
+ ConfigurationSection items_section = chest_section.getConfigurationSection("items");
+ for (String item_name : items_section.getKeys(false)) {
+ if (debug_flag) { System.out.println("[DEBUG] Item Key: " + item_name); }
+ // Parse drop chance
+ Material material;
+ ConfigurationSection item_section = items_section.getConfigurationSection(item_name);
+ String drop_chance_string = item_section.getString("drop_chance");
+ String[] parts = drop_chance_string.split("/");
+ int numerator = Integer.parseInt(parts[0]);
+ int denominator = Integer.parseInt(parts[1]);
+ if (ThreadLocalRandom.current().nextInt(1, denominator + 1) <= numerator) {
+ NBTCompound item = new NBTCompound();
+ short damage;
+
+ // Check if the item is defined in items.yml
+ ConfigurationSection custom_item_section = plugin.get_items_config().getConfigurationSection("items." + item_name);
+ if (custom_item_section != null) {
+ // This is a custom item, load its data from items.yml
+ // Parse the item ID
+ String id = custom_item_section.getString("id");
+ if (id == null) {
+ sender.sendMessage(ChatColor.RED + "Item " + item_name + " does not have an id.");
+ continue;
+ }
+ material = Material.matchMaterial(id);
+ if (material == null) {
+ sender.sendMessage(ChatColor.RED + "Item " + item_name + " invalid custom item ID. Failed to match " + id + " to material.");
+ continue;
+ }
+ item.put("id", material.getId());
+
+ // Parse the display name
+ NBTCompound tag_NBT = new NBTCompound();
+ ConfigurationSection display_section = custom_item_section.getConfigurationSection("display");
+ if (display_section != null) {
+ NBTCompound display_NBT = new NBTCompound(); // Create a new NBTCompound for the display
+ String name = display_section.getString("Name");
+ if (name != null) {
+ name = name.replace('&', '§');
+ display_NBT.put("Name", name);
+ }
+ List lore_lines = display_section.getStringList("Lore");
+ if (lore_lines != null && !lore_lines.isEmpty()) {
+ NBTList lore_NBT = new NBTList();
+ for (String line : lore_lines) {
+ line = line.replace('&', '§');
+ lore_NBT.add(line);
+ }
+ display_NBT.put("Lore", lore_NBT);
+ }
+ // Parse the color
+ if (display_section.contains("color")) {
+ display_NBT.put("color", display_section.getInt("color"));
+ }
+ // If the display NBTCompound is not empty, add it to the item's tag NBTCompound
+ if (!display_NBT.isEmpty()) {
+ tag_NBT.put("display", display_NBT);
+ }
+ }
+
+ ConfigurationSection enchantments_section = custom_item_section.getConfigurationSection("enchantments");
+ if (enchantments_section != null) {
+ NBTList ench_NBT = new NBTList();
+ for (String enchantment_name : enchantments_section.getKeys(false)) {
+ ConfigurationSection enchantment_section = enchantments_section.getConfigurationSection(enchantment_name);
+ NBTCompound enchantment_NBT = new NBTCompound();
+ NBTCompound ench_item = new NBTCompound();
+ ench_item.put("id", enchantments.get_id(enchantment_name));
+ ench_item.put("lvl", enchantment_section.getInt("lvl"));
+ ench_NBT.add(ench_item);
+ }
+ // If the ench NBTList is not empty, add it to the item's tag NBTCompound
+ if (!ench_NBT.isEmpty()) {
+ if (tag_NBT.isEmpty()) {
+ tag_NBT = new NBTCompound();
+ }
+ tag_NBT.put("ench", ench_NBT);
+ }
+ }
+
+
+
+
+ // Parse the LOTREnch list
+ if (custom_item_section.contains("LOTREnch")) {
+ List lotr_ench_list = custom_item_section.getStringList("LOTREnch");
+ if (lotr_ench_list != null && !lotr_ench_list.isEmpty()) {
+ NBTList lotr_ench_NBT = new NBTList();
+ for (String ench : lotr_ench_list) {
+ lotr_ench_NBT.add(ench);
+ }
+ tag_NBT.put("LOTREnch", lotr_ench_NBT);
+ }
+ }
+
+ // Parse the LOTRRandomEnch byte
+ if (custom_item_section.contains("LOTRRandomEnch")) {
+ tag_NBT.put("LOTRRandomEnch", (byte) custom_item_section.getInt("LOTRRandomEnch"));
+ }
+
+ // Parse the LOTRRepairCost int
+ if (custom_item_section.contains("LOTRRepairCost")) {
+ tag_NBT.put("LOTRRepairCost", (int) custom_item_section.getInt("LOTRRepairCost"));
+ }
+ if (custom_item_section.contains("RobesColor")) {
+ tag_NBT.put("RobesColor", (int) custom_item_section.getInt("RobesColor"));
+ }
+ if (custom_item_section.contains("HatColor")) {
+ tag_NBT.put("HatColor", (int) custom_item_section.getInt("HatColor"));
+ }
+ if (custom_item_section.contains("PouchColor")) {
+ tag_NBT.put("PouchColor", (int) custom_item_section.getInt("PouchColor"));
+ }
+
+ if (!tag_NBT.isEmpty()) {
+ item.put("tag", tag_NBT);
+ }
+ if (!custom_item_section.contains("damage")) {
+ damage = (short) 0;
+ } else {
+ damage = (short) custom_item_section.getInt("damage");
+ }
+ } else {
+ // This is a standard item, use Bukkit to get its ID
+ material = Material.matchMaterial(item_name);
+ if (material == null) {
+ sender.sendMessage(ChatColor.RED + "Item " + item_name + " not found.");
+ continue;
+ }
+ damage = (short) item_section.getInt("damage");
+ item.put("id", material.getId());
+ }
+
+ // Generate random quantity within specified range
+ int min_count = item_section.getInt("min_count", -1);
+ int max_count = item_section.getInt("max_count", -1);
+ byte count;
+
+ if (min_count != -1 && max_count != -1) {
+ count = (byte) ThreadLocalRandom.current().nextInt(min_count, max_count + 1);
+ } else {
+ count = (byte) item_section.getInt("count");
+ }
+
+ byte slot = (byte) item_section.getInt("slot");
+
+ if (debug_flag) { System.out.println("[DEBUG] Item data: id=" + material.getId() + ", damage=" + damage + ", count=" + count + ", slot=" + slot); } // Logging statement
+
+ item.put("id", material.getId());
+ item.put("Damage", damage);
+ item.put("Count", count);
+ item.put("Slot", slot);
+
+ items.add(item);
+ } else {
+ if (!silent_flag) {System.out.println("Did not add item: " + item_name);}
+ }
+ }
+
+ // Get the chest type
+ String chest_type_name = chest_section.getString("chest_type");
+ // Get the actual Minecraft block name for the chest type
+ String chest_type_block_name = config.getString("chest_types." + chest_type_name);
+ Material chestType = Material.matchMaterial(chest_type_block_name);
+ if (chestType == null) {
+ sender.sendMessage(ChatColor.RED + "Chest type " + chest_type_name + " not found.");
+ return true;
+ } else {
+ if (debug_flag) sender.sendMessage("Parsing chestFacingDirection with args.length " + args.length + ", argument_offset " + argument_offset + " and final value:");
+ if (debug_flag) sender.sendMessage((args.length == 6 + argument_offset) ? args[5 + argument_offset] : "north");
+ String chestFacingDirection = (args.length == 6 + argument_offset) ? args[5 + argument_offset] : "north";
+ BlockFace chestFacing = null;
+ switch (chestFacingDirection.toLowerCase()) {
+ case "north":
+ chestFacing = BlockFace.NORTH;
+ break;
+ case "south":
+ chestFacing = BlockFace.SOUTH;
+ break;
+ case "west":
+ chestFacing = BlockFace.WEST;
+ break;
+ case "east":
+ chestFacing = BlockFace.EAST;
+ break;
+ default:
+ sender.sendMessage(ChatColor.RED + "Invalid chest facing direction. Available options: north, south, west, east.");
+ return true;
+ }
+
+ // Set the block at the specified location to a chest
+ Block block = world.getBlockAt(x, y, z);
+ block.setType(chestType);
+
+ byte data;
+ switch (chestFacingDirection.toLowerCase()) {
+ case "north":
+ data = 2;
+ break;
+ case "south":
+ data = 3;
+ break;
+ case "west":
+ data = 4;
+ break;
+ case "east":
+ data = 5;
+ break;
+ default:
+ sender.sendMessage(ChatColor.RED + "Invalid chest facing direction. Available options: north, south, west, east.");
+ return true;
+ }
+ block.setData(data);
+
+ NBTCompound chest_data = NBT_manager.read(block);
+ if (debug_flag) { System.out.println("[DEBUG] Original chest data: " + chest_data); } // Logging statement
+ chest_data.put("Items", items);
+ if (debug_flag) { System.out.println("[DEBUG] Modified chest data: " + chest_data); } // Logging statement
+ NBT_manager.write(block, chest_data);
+
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/zivilon/dungeontools/commands/playsound_command.java b/src/main/java/com/zivilon/dungeontools/commands/playsound_command.java
new file mode 100644
index 0000000..7232451
--- /dev/null
+++ b/src/main/java/com/zivilon/dungeontools/commands/playsound_command.java
@@ -0,0 +1,177 @@
+package com.zivilon.dungeontools.commands;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Sound;
+import org.bukkit.World;
+import org.bukkit.ChatColor;
+import org.bukkit.entity.Player;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabCompleter;
+import org.bukkit.util.StringUtil;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+public class playsound_command implements CommandExecutor, TabCompleter {
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+
+ if (!sender.hasPermission("dungeontools.playsound")) {
+ sender.sendMessage(ChatColor.RED + "You do not have permission to use this command.");
+ return true;
+ }
+
+ if (args.length < 8) {
+ sender.sendMessage("Not enough arguments! Usage: /playsound_loc [-d -s -g] ");
+ return false;
+ }
+
+
+ boolean debug_flag = false;
+ boolean silent_flag = false;
+ boolean global_flag = false;
+ int argument_offset = 0; // this offset helps us handle the arguments correctly
+
+ // Iterate over flags
+ for (; argument_offset < args.length; argument_offset++) {
+ if (args[argument_offset].startsWith("-")) {
+ debug_flag |= args[argument_offset].contains("d");
+ silent_flag |= args[argument_offset].contains("s");
+ global_flag |= args[argument_offset].contains("g");
+ } else {
+ break;
+ }
+ }
+
+ if (args.length < (8 + argument_offset)) {
+ sender.sendMessage("Not enough arguments! Usage: /playsound_loc [-d -s -g] ");
+ return false;
+ }
+
+ String sound_name = args[0 + argument_offset];
+
+ World world = Bukkit.getWorld(args[1 + argument_offset]);
+ if (world == null) {
+ sender.sendMessage("Invalid world name!");
+ return false;
+ }
+
+ double x;
+ try {
+ x = Double.parseDouble(args[2 + argument_offset]);
+ } catch (NumberFormatException e) {
+ sender.sendMessage("Invalid X coordinate! Must be a number.");
+ return false;
+ }
+
+ double y;
+ try {
+ y = Double.parseDouble(args[3 + argument_offset]);
+ } catch (NumberFormatException e) {
+ sender.sendMessage("Invalid Y coordinate! Must be a number.");
+ return false;
+ }
+
+ double z;
+ try {
+ z = Double.parseDouble(args[4 + argument_offset]);
+ } catch (NumberFormatException e) {
+ sender.sendMessage("Invalid Z coordinate! Must be a number.");
+ return false;
+ }
+ float volume;
+ try {
+ volume = Float.parseFloat(args[5 + argument_offset]);
+ } catch (NumberFormatException e) {
+ sender.sendMessage("Invalid volume! Must be a number.");
+ return false;
+ }
+ float pitch;
+ try {
+ pitch = Float.parseFloat(args[6 + argument_offset]);
+ } catch (NumberFormatException e) {
+ sender.sendMessage("Invalid pitch! Must be a number.");
+ return false;
+ }
+ double radius;
+ try {
+ radius = Double.parseDouble(args[7 + argument_offset]);
+ } catch (NumberFormatException e) {
+ sender.sendMessage("Invalid radius! Must be a number.");
+ return false;
+ }
+
+
+ double radius_squared = Math.pow(radius, 2);
+
+ Location sound_location = new Location(world, x, y, z);
+
+ if (global_flag) {
+ for (Player player : Bukkit.getOnlinePlayers()) {
+ if (player.getLocation().distanceSquared(sound_location) <= radius_squared) {
+ Location player_location = player.getLocation();
+ String commandString = String.format("fakeplayer_run playsound %s %s %f %f %f %f %f",
+ sound_name, player.getName(), player_location.getX(), player_location.getY(), player_location.getZ(), volume, pitch);
+ if (debug_flag) System.out.println("Running command " + commandString);
+ Bukkit.dispatchCommand(Bukkit.getConsoleSender(), commandString);
+ }
+ }
+ } else {
+ for (Player player : Bukkit.getOnlinePlayers()) {
+ if (player.getLocation().distanceSquared(sound_location) <= radius_squared) {
+ String commandString = String.format("fakeplayer_run playsound %s %s %f %f %f %f %f",
+ sound_name, player.getName(), x, y, z, volume, pitch);
+ if (debug_flag) System.out.println("Running command " + commandString);
+ Bukkit.dispatchCommand(Bukkit.getConsoleSender(), commandString);
+ }
+ }
+ }
+
+
+
+ return true;
+ }
+
+ List moddedSounds = Arrays.asList("lotr:elf.male.say", "lotr:elf.male.attack", "lotr:elf.woodElf_teleport");
+
+ @Override
+ public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
+ List completions = new ArrayList<>();
+ List commands = new ArrayList<>();
+
+ int argument_offset = 0; // this offset helps us handle the arguments correctly
+
+ // Iterate over flags
+ for (; argument_offset < args.length; argument_offset++) {
+ if (!args[argument_offset].startsWith("-")) {
+ break;
+ }
+ }
+
+ if (args.length == (1 + argument_offset)) {
+ // If the player is typing the first non-flag argument (sound name),
+ // suggest names of all available sounds.
+
+ // Add all sounds from Bukkit
+ for (Sound sound : Sound.values()) {
+ commands.add(sound.name().toLowerCase());
+ }
+
+ // Add all modded sounds
+ commands.addAll(moddedSounds);
+
+ StringUtil.copyPartialMatches(args[argument_offset], commands, completions);
+
+ Collections.sort(completions);
+ }
+ return completions;
+ }
+
+
+}
diff --git a/src/main/java/com/zivilon/dungeontools/listeners/aggression_listener.java b/src/main/java/com/zivilon/dungeontools/listeners/aggression_listener.java
new file mode 100644
index 0000000..d2e415a
--- /dev/null
+++ b/src/main/java/com/zivilon/dungeontools/listeners/aggression_listener.java
@@ -0,0 +1,89 @@
+package com.zivilon.dungeontools.listeners;
+
+import me.dpohvar.powernbt.api.NBTCompound;
+import me.dpohvar.powernbt.api.NBTList;
+import me.dpohvar.powernbt.api.NBTManager;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Creature;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityDamageByEntityEvent;
+import org.bukkit.scheduler.BukkitRunnable;
+
+import java.util.Random;
+
+import com.zivilon.dungeontools.DungeonTools;
+
+public class aggression_listener implements Listener {
+ public final Random random = new Random();
+ public final NBTManager NBT_manager = me.dpohvar.powernbt.PowerNBT.getApi();
+ public final DungeonTools plugin;
+
+ public aggression_listener(DungeonTools dungeontools) {
+ this.plugin = dungeontools;
+ }
+
+
+ @EventHandler
+ public void onEntityDamageByEntity(EntityDamageByEntityEvent event) {
+ Entity entity = event.getEntity();
+ if (entity instanceof Creature) {
+ NBTCompound entityNBT = NBT_manager.read(entity);
+ NBTCompound forgeData = entityNBT.getCompound("ForgeData");
+
+ if (forgeData != null && forgeData.get("reset_aggression") != null) { // Check if forgeData is not null
+ float resetProbability = forgeData.getFloat("reset_aggression");
+ if (random.nextFloat() < resetProbability) resetTarget((Creature) entity);
+ }
+ }
+ }
+
+ public void resetTarget(Creature creature) {
+ NBTCompound entityNBT = NBT_manager.read(creature);
+ NBTList attributes = entityNBT.getList("Attributes");
+ for (Object attribute : attributes) {
+ NBTCompound attributeCompound = (NBTCompound) attribute;
+ if (String.valueOf(attributeCompound.get("Name")).equals("generic.followRange")) {
+ // Store the original follow range
+ double original_follow_range = attributeCompound.getDouble("Base");
+
+ // Set to 0 temporarily
+ attributeCompound.put("Base", 0.0);
+ NBT_manager.write(creature, entityNBT);
+
+ NBTCompound entityNBT2 = NBT_manager.read(creature);
+ NBTList attributes2 = entityNBT2.getList("Attributes");
+
+ final Double finalOriginalFollowRange = original_follow_range;
+ final Creature finalCreature = creature;
+
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ NBTCompound currentEntityNBT = NBT_manager.read(finalCreature);
+ NBTList currentAttributes = currentEntityNBT.getList("Attributes");
+
+ for (Object currentAttribute : currentAttributes) {
+ NBTCompound currentAttributeCompound = (NBTCompound) currentAttribute;
+ if (String.valueOf(currentAttributeCompound.get("Name")).equals("generic.followRange")) {
+ // Set Base value to original_follow_range
+ NBTCompound entityNBT2 = NBT_manager.read(finalCreature);
+ NBTList attributes2 = entityNBT2.getList("Attributes");
+
+ currentAttributeCompound.put("Base", finalOriginalFollowRange);
+ NBT_manager.write(finalCreature, currentEntityNBT);
+ break;
+ }
+ }
+ }
+ }.runTaskLater(this.plugin, 2); // Restore follow range 2 ticks later. Needs delay for the entity to have time to lose target
+ break;
+ }
+ }
+ }
+
+
+}
diff --git a/src/main/java/com/zivilon/dungeontools/listeners/barrow_wight_teleport_listener.java b/src/main/java/com/zivilon/dungeontools/listeners/barrow_wight_teleport_listener.java
new file mode 100644
index 0000000..a2b4635
--- /dev/null
+++ b/src/main/java/com/zivilon/dungeontools/listeners/barrow_wight_teleport_listener.java
@@ -0,0 +1,76 @@
+package com.zivilon.dungeontools.listeners;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityDamageEvent;
+
+public class barrow_wight_teleport_listener implements Listener {
+
+ // Target teleport location
+ private final Location teleport_location = new Location(
+ Bukkit.getWorld("DIM100"), // Change to your actual world name
+ -100203.5,
+ 108.0,
+ 46030.5
+ );
+
+ // Bounding box of the area from where the Barrow-wight should not be teleported
+ private final Location corner_1 = new Location(
+ teleport_location.getWorld(),
+ -100227,
+ 90,
+ 46021
+ );
+
+ private final Location corner_2 = new Location(
+ teleport_location.getWorld(),
+ -100173,
+ 150,
+ 46052
+ );
+
+ @EventHandler
+ public void onEntityDamage(EntityDamageEvent event) {
+ Entity entity = event.getEntity();
+
+ if (entity.getType() == EntityType.valueOf("LOTR_BARROWWIGHT")) { // Change to the actual EntityType name
+ Location entity_location = entity.getLocation();
+
+ if (Math.abs(entity_location.getX() - teleport_location.getX()) <= 200 &&
+ Math.abs(entity_location.getZ() - teleport_location.getZ()) <= 200) {
+
+ if (isOutsideBox(entity_location, corner_1, corner_2)) {
+ if (entity instanceof LivingEntity) {
+ LivingEntity livingEntity = (LivingEntity) entity;
+
+ // Heal the entity to max health
+ livingEntity.setHealth(livingEntity.getMaxHealth());
+ }
+ event.setCancelled(true);
+ entity.teleport(teleport_location);
+ }
+ }
+ }
+ }
+
+ private boolean isOutsideBox(Location point, Location corner_1, Location corner_2) {
+ boolean return_value = false;
+ if (point.getX() < corner_1.getX() || point.getX() > corner_2.getX()) {
+ return_value = true;
+ }
+ if (point.getY() < corner_1.getY() || point.getY() > corner_2.getY()) {
+ return_value = true;
+ }
+ if (point.getZ() < corner_1.getZ() || point.getZ() > corner_2.getZ()) {
+ return_value = true;
+ }
+
+ return return_value;
+ }
+}
diff --git a/src/main/java/com/zivilon/dungeontools/listeners/kill_tracker_listener.java b/src/main/java/com/zivilon/dungeontools/listeners/kill_tracker_listener.java
new file mode 100644
index 0000000..330ca3a
--- /dev/null
+++ b/src/main/java/com/zivilon/dungeontools/listeners/kill_tracker_listener.java
@@ -0,0 +1,68 @@
+package com.zivilon.dungeontools.listeners;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityDeathEvent;
+import org.bukkit.event.entity.PlayerDeathEvent;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.entity.Player;
+
+import java.util.Map;
+
+import com.zivilon.dungeontools.Area;
+
+public class kill_tracker_listener implements Listener {
+ private Map tracking_areas;
+
+ public kill_tracker_listener(Map tracking_areas) {
+ this.tracking_areas = tracking_areas;
+ }
+
+ @EventHandler
+ public void onEntityDeath(EntityDeathEvent event) {
+ Location location = event.getEntity().getLocation();
+
+ for (Map.Entry entry : tracking_areas.entrySet()) {
+ Area area = entry.getValue();
+
+ if (area.contains(location)) {
+
+ if (event.getEntity() instanceof Player) {
+
+ // The player is in this area.
+ String playerDeathCommand = area.getPlayerDeathCommand();
+ if (playerDeathCommand != null) {
+
+ // Check for other players in the area
+ for (Player otherPlayer : Bukkit.getOnlinePlayers()) {
+ if (otherPlayer.equals(event.getEntity())) {
+ continue;
+ }
+ Location otherLocation = otherPlayer.getLocation();
+ if (area.contains(otherLocation)) {
+ // There is another player in the area.
+ return;
+ }
+ }
+ // No other players in the area. Execute the command.
+ Bukkit.dispatchCommand(Bukkit.getConsoleSender(), playerDeathCommand);
+ }
+ } else if (area.containsEntity((LivingEntity)event.getEntity())) {
+
+ // Then, increment the count.
+ area.incrementCount();
+
+ // First, get the command for the current count.
+ String command = area.getCommandForCount();
+
+ // Then, if the command is not null, run it.
+ if (command != null) {
+ Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/zivilon/dungeontools/utilities/enchantments.java b/src/main/java/com/zivilon/dungeontools/utilities/enchantments.java
new file mode 100644
index 0000000..a583032
--- /dev/null
+++ b/src/main/java/com/zivilon/dungeontools/utilities/enchantments.java
@@ -0,0 +1,39 @@
+package com.zivilon.dungeontools.utilities;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class enchantments {
+ public static final Map enchantment_ids = new HashMap<>();
+
+ static {
+ enchantment_ids.put("protection", 0);
+ enchantment_ids.put("fire_protection", 1);
+ enchantment_ids.put("fall_protection", 2);
+ enchantment_ids.put("blast_protection", 3);
+ enchantment_ids.put("projectile_protection", 4);
+ enchantment_ids.put("oxygen", 5);
+ enchantment_ids.put("aqua_affinity", 6);
+ enchantment_ids.put("thorns", 7);
+ enchantment_ids.put("sharpness", 16);
+ enchantment_ids.put("smite", 17);
+ enchantment_ids.put("bane_of_arthropods", 18);
+ enchantment_ids.put("knockback", 19);
+ enchantment_ids.put("fire_aspect", 20);
+ enchantment_ids.put("looting", 21);
+ enchantment_ids.put("efficiency", 32);
+ enchantment_ids.put("silk_touch", 33);
+ enchantment_ids.put("unbreaking", 34);
+ enchantment_ids.put("fortune", 35);
+ enchantment_ids.put("power", 48);
+ enchantment_ids.put("punch", 49);
+ enchantment_ids.put("flame", 50);
+ enchantment_ids.put("infinity", 51);
+ }
+
+ public static Integer get_id(String enchantment_name) {
+ String lookupKey = enchantment_name.toLowerCase().replace(" ", "_");
+ Integer id = enchantment_ids.get(lookupKey);
+ return id;
+ }
+}
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
new file mode 100644
index 0000000..1e3e91b
--- /dev/null
+++ b/src/main/resources/config.yml
@@ -0,0 +1,17 @@
+chests:
+ chest1:
+ minecraft:diamond_sword:
+ drop_chance: 1/2
+ damage: 0
+ count: 1
+ slot: 0
+ minecraft:diamond:
+ drop_chance: 1/3
+ damage: 0
+ count: 5
+ slot: 1
+ minecraft:golden_apple:
+ drop_chance: 1/4
+ damage: 0
+ count: 3
+ slot: 2
\ No newline at end of file
diff --git a/src/main/resources/items.yml b/src/main/resources/items.yml
new file mode 100644
index 0000000..ab55bbe
--- /dev/null
+++ b/src/main/resources/items.yml
@@ -0,0 +1,37 @@
+items:
+ special_sword:
+ id: DIAMOND_SWORD
+ display:
+ Name: "Special \"Sword\""
+ Lore:
+ - "This is first line of lore"
+ - "This is second line of lore"
+ - "This is third line of lore"
+ enchantments:
+ sharpness:
+ id: "sharpness"
+ lvl: 5
+ unbreaking:
+ id: "unbreaking"
+ lvl: 3
+ shiny_robes:
+ id: LOTR_ITEMBODYHARADROBES
+ enchantments:
+ infinity:
+ id: "infinity"
+ lvl: 1
+ RobesColor: 16711680
+ shiny_pouch:
+ id: LOTR_ITEMPOUCH
+ enchantments:
+ infinity:
+ id: "infinity"
+ lvl: 1
+ PouchColor: 255
+ shiny_hat:
+ id: LOTR_ITEMPARTYHAT
+ enchantments:
+ infinity:
+ id: "infinity"
+ lvl: 1
+ HatColor: 65280
diff --git a/src/main/resources/loot_chests.yml b/src/main/resources/loot_chests.yml
new file mode 100644
index 0000000..459da7a
--- /dev/null
+++ b/src/main/resources/loot_chests.yml
@@ -0,0 +1,2 @@
+# Currently useless. Will eventually replace config.yml
+# Not sure why I put everything in config.yml but once I find other use for it, loot chests go here
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
new file mode 100644
index 0000000..b51a00e
--- /dev/null
+++ b/src/main/resources/plugin.yml
@@ -0,0 +1,13 @@
+main: com.zivilon.dungeontools.DungeonTools
+name: DungeonTools
+author: Shinare
+version: 1.4
+depend: [FakePlayer]
+commands:
+ loot_chest:
+ description: Creates a loot chest in specified location
+ track_kills:
+ description: Tracks kills within specified area
+ playsound_loc:
+ description: Plays a sound in the given area.
+ Usage: /playsound_loc