diff --git a/src/main/java/eu/m724/giants/Configuration.java b/src/main/java/eu/m724/giants/Configuration.java index f7bbed6..3eeee69 100644 --- a/src/main/java/eu/m724/giants/Configuration.java +++ b/src/main/java/eu/m724/giants/Configuration.java @@ -1,8 +1,8 @@ package eu.m724.giants; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.Plugin; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; @@ -11,6 +11,7 @@ import org.bukkit.util.Vector; import java.io.File; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.logging.Logger; @@ -69,23 +70,26 @@ public class Configuration { } } - for (String line : config.getStringList("drops")) { - String[] parts = line.split(":"); - + for (Map dropMap : config.getMapList("drops")) { try { - Material material = Material.getMaterial(parts[0]); - if (material == null) { - throw new IllegalArgumentException("Invalid Material"); + ItemStack itemStack; + if (dropMap.containsKey("itemStack")) { + itemStack = (ItemStack) dropMap.get("itemStack"); + } else { + Material material = Material.getMaterial((String) dropMap.get("material")); + if (material == null) { + throw new IllegalArgumentException("Invalid Material"); + } + itemStack = new ItemStack(material, 1); } - int min = Integer.parseInt(parts[1]); - int max = Integer.parseInt(parts[2]); - float chance = Integer.parseInt(parts[3]) / 100f; // TODO is there even a point - Drop drop = new Drop(material, min, max, chance); - drops.add(drop); + int min = (int) dropMap.get("quantityMin"); + int max = (int) dropMap.get("quantityMax"); + float chance = (int) dropMap.get("chance"); + + drops.add(new Drop(itemStack, min, max, chance)); } catch (IllegalArgumentException e) { logger.warning("Parsing a drop failed:"); - logger.warning(line); logger.warning(e.getMessage()); } } diff --git a/src/main/java/eu/m724/giants/Drop.java b/src/main/java/eu/m724/giants/Drop.java index 21cc41d..b2e8cdc 100644 --- a/src/main/java/eu/m724/giants/Drop.java +++ b/src/main/java/eu/m724/giants/Drop.java @@ -1,16 +1,25 @@ package eu.m724.giants; -import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import java.util.concurrent.ThreadLocalRandom; public class Drop { - public final Material material; + public final ItemStack itemStack; public final int min, max; public final float chance; - public Drop(Material material, int min, int max, float chance) { - this.material = material; + public Drop(ItemStack itemStack, int min, int max, float chance) { + this.itemStack = itemStack; this.min = min; this.max = max; this.chance = chance; } + + public ItemStack generateItemStack() { + int amount = ThreadLocalRandom.current().nextInt(min, max + 1); + ItemStack itemStack = this.itemStack.clone(); + itemStack.setAmount(amount); + return itemStack; + } } diff --git a/src/main/java/eu/m724/giants/GiantProcessor.java b/src/main/java/eu/m724/giants/GiantProcessor.java index d5b3643..40b5396 100644 --- a/src/main/java/eu/m724/giants/GiantProcessor.java +++ b/src/main/java/eu/m724/giants/GiantProcessor.java @@ -17,14 +17,15 @@ import org.bukkit.scheduler.BukkitRunnable; import java.util.*; import java.util.concurrent.ThreadLocalRandom; -import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Collectors; +// TODO move ai stuff to another class /** * A processor class that processes giants */ public class GiantProcessor implements Listener { + private static final int VERSION = 1; + private final JavaPlugin plugin; private final Configuration configuration; private final Logger logger; @@ -43,36 +44,38 @@ public class GiantProcessor implements Listener { } void start() { - new BukkitRunnable() { - @Override - public void run() { - for (Husk husk : Set.copyOf(trackedHusks)) { - if (husk.isValid()) { - Location location = husk.getLocation(); - Entity giant = husk.getVehicle(); + if (configuration.ai) { + new BukkitRunnable() { + @Override + public void run() { + for (Husk husk : Set.copyOf(trackedHusks)) { + if (husk.isValid()) { + Location location = husk.getLocation(); + Entity giant = husk.getVehicle(); - if (giant instanceof Giant) { - if (giant.isValid()) { // TODO reconsider - giant.getWorld().getNearbyEntities( - giant.getBoundingBox().expand(configuration.attackReach), - e -> (e instanceof Player && !e.isInvulnerable()) - ).forEach(p -> ((Player) p).damage(configuration.attackDamage, giant)); - giant.setRotation(location.getYaw(), location.getPitch()); + if (giant instanceof Giant) { + if (giant.isValid()) { // TODO reconsider + giant.getWorld().getNearbyEntities( + giant.getBoundingBox().expand(configuration.attackReach), + e -> (e instanceof Player && !e.isInvulnerable()) + ).forEach(p -> ((Player) p).damage(configuration.attackDamage, giant)); + giant.setRotation(location.getYaw(), location.getPitch()); + } + } else { + // no vehicle means the giant doesn't exist anymore and the husk should also not exist + husk.setHealth(0); + + trackedHusks.remove(husk); + logger.fine("Husk killed because Giant died at " + husk.getLocation()); } } else { - // no vehicle means the giant doesn't exist anymore and the husk should also not exist - husk.setHealth(0); - trackedHusks.remove(husk); - logger.fine("Husk killed because Giant died at " + husk.getLocation()); + logger.fine("Husk unloaded at " + husk.getLocation()); } - } else { - trackedHusks.remove(husk); - logger.fine("Husk unloaded at " + husk.getLocation()); } } - } - }.runTaskTimer(plugin, configuration.attackDelay, 0L); + }.runTaskTimer(plugin, configuration.attackDelay, 0L); + } plugin.getServer().getPluginManager().registerEvents(this, plugin); } @@ -86,8 +89,7 @@ public class GiantProcessor implements Listener { new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 1).apply(passenger); passenger.setInvulnerable(true); passenger.setPersistent(true); - // TODO I was considering making the value plugin version to track and correct changes - passenger.getPersistentDataContainer().set(huskKey, PersistentDataType.BOOLEAN, true); + passenger.getPersistentDataContainer().set(huskKey, PersistentDataType.INTEGER, VERSION); entity.addPassenger(passenger); trackedHusks.add((Husk) passenger); @@ -157,7 +159,7 @@ public class GiantProcessor implements Listener { logger.fine("Rolling a drop"); if (drop.chance > random.nextFloat()) { - ItemStack is = new ItemStack(drop.material, random.nextInt(drop.min, drop.max + 1)); + ItemStack is = drop.generateItemStack(); entity.getWorld().dropItemNaturally(location, is); logger.fine("Dropped " + is); diff --git a/src/main/java/eu/m724/giants/GiantsCommand.java b/src/main/java/eu/m724/giants/GiantsCommand.java new file mode 100644 index 0000000..717c9df --- /dev/null +++ b/src/main/java/eu/m724/giants/GiantsCommand.java @@ -0,0 +1,80 @@ +package eu.m724.giants; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class GiantsCommand implements CommandExecutor { + private final GiantsPlugin plugin; + + public GiantsCommand(GiantsPlugin plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (args.length == 0) { + sender.sendMessage("Giants " + plugin.getDescription().getVersion()); + return true; + } + + String action = args[0]; + + if (!sender.hasPermission("giants.command." + action)) { + sender.sendMessage("You don't have permission to use this command, or it doesn't exist."); + return true; + } + + Player player = sender instanceof Player ? (Player) sender : null; + + if (action.equals("spawn")) { + if (player != null) { + plugin.spawnGiant(player.getLocation()); + sender.sendMessage("Spawned a Giant"); + } else { + sender.sendMessage("Only players can use this command."); + } + } else if (action.equals("serialize")) { + if (player != null) { + ItemStack itemStack = player.getInventory().getItemInMainHand(); + + List> list = new ArrayList<>(); + Map map = new LinkedHashMap<>(); + map.put("chance", 1); + map.put("quantityMin", itemStack.getAmount()); + map.put("quantityMax", itemStack.getAmount()); + map.put("itemStack", itemStack); + list.add(map); + + YamlConfiguration yamlConfiguration = new YamlConfiguration(); + yamlConfiguration.set("v", list); + + yamlConfiguration.setInlineComments("v", List.of("Copy the below content to your config.yml")); + + long now = System.currentTimeMillis(); + String name = "item-" + now + ".yml"; + try { + yamlConfiguration.save(new File(plugin.getDataFolder(), name)); + sender.sendMessage("Saved to plugins/Giants/" + name + ". To add it as a drop, see instructions in the file."); + } catch (IOException e) { + e.printStackTrace(); + sender.sendMessage("Error saving file. See console for details."); + } + } else { + sender.sendMessage("Only players can use this command."); + } + } + + return true; + } +} diff --git a/src/main/java/eu/m724/giants/GiantsPlugin.java b/src/main/java/eu/m724/giants/GiantsPlugin.java index 576129a..711a22f 100644 --- a/src/main/java/eu/m724/giants/GiantsPlugin.java +++ b/src/main/java/eu/m724/giants/GiantsPlugin.java @@ -1,12 +1,9 @@ package eu.m724.giants; import org.bukkit.Location; -import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; import org.bukkit.entity.Giant; import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; import java.io.File; @@ -25,23 +22,11 @@ public class GiantsPlugin extends JavaPlugin implements CommandExecutor { configuration.load(); - getCommand("spawngiant").setExecutor(this); + getCommand("giants").setExecutor(new GiantsCommand(this)); giantProcessor.start(); } - @Override - public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { - try { - Player player = (Player) sender; - giantProcessor.spawnGiant(player.getLocation()); - } catch (ClassCastException e) { - sender.sendMessage("Only players can use this command."); - } - - return true; - } - // TODO api, untested /** diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 099a4ed..fa9ca98 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,12 +1,9 @@ # Notes: # To have no values in a list, remove every value below it and change to [] like `effects: []` -# If disabled, the giant won't move (but it will still attack) +# If disabled, the giant will not move or attack ai: true -# See above -attack: true - # In hearts, 0.5 is half a heart attackDamage: 1.0 @@ -27,7 +24,8 @@ expandUp: 0 # 0 is 0% (no zombie becomes a giant), 1 is 100% (every zombie becomes a giant), so the default 0.005 is 0.5% chance: 0.005 -# Which worlds not to spawn Giants in +# Worlds that Giants will not spawn in naturally. +# You can still use the command to spawn blacklist: - "world_nether" - "world_the_end" @@ -42,10 +40,14 @@ effects: - "HEALTH_BOOST:10" # Custom drops -# material:min quantity:max quantity:chance -# material: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html -# min and max are inclusive -# unlike above, chance is an integer from 0 to 100 -# metadata can't be set yet +# Adding metadata (enchantments, lore etc) is possible. See https://www.spigotmc.org/resources/giants.99600/field?field=documentation drops: - - "APPLE:1:3:25" # 25% of the time, drop 1 to 3 apples + - material: STICK # See https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html + chance: 0.75 # 75% chance to drop 1-3 apples + quantityMin: 1 # inclusive + quantityMax: 3 # inclusive + - material: WOODEN_SWORD + chance: 0.05 # 5% + quantityMin: 1 + quantityMax: 1 + diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 78891e5..c7fdeca 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -7,11 +7,12 @@ website: https://www.spigotmc.org/resources/giants.99600/ load: STARTUP commands: - spawngiant: - description: Spawns a Giant at your location. - permission: giants.spawngiant - permission-message: You do not have permission to use this command. + giants: + description: Utility command for Giants permissions: - giants.spawngiant: - description: Permits /spawngiant + giants.command.spawn: + description: Permits /giants spawn + default: op + giants.command.serialize: + description: Permits /giants serialize default: op \ No newline at end of file