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