From 2d4db1d36c9ddcfb8190f9fc1297337b0c7d04b6 Mon Sep 17 00:00:00 2001 From: Gunging <48371009+Gunging@users.noreply.github.com> Date: Sat, 5 Feb 2022 19:32:37 -0600 Subject: [PATCH] + Now allows for timer skill triggers https://git.mythiccraft.io/mythiccraft/mmoitems/-/wikis/Ability-Triggers + New custom MythicMob mechanics https://git.mythiccraft.io/mythiccraft/mmoitems/-/wikis/MythicMob-Mechanics + Can now use item filters to produce vanilla or MythicItems in MMOItems Stations, as well as accept them as ingredients, bottom of the page https://git.mythiccraft.io/mythiccraft/mmoitems/-/wikis/Recipe-Ingredients To use items of **ItemsAdder** or **SlimeFun** plugins in MMOItems crafting recipes and stations, contact @Ayano#2372, they have made an addon jar that adds the item filters for these plugins to MythicLib. --- pom.xml | 2 +- .../java/net/Indyuce/mmoitems/MMOItems.java | 13 +- .../java/net/Indyuce/mmoitems/MMOUtils.java | 5 +- .../mmoitems/api/crafting/ConfigMMOItem.java | 7 +- .../api/crafting/CraftingStation.java | 2 +- .../api/crafting/ingredient/Ingredient.java | 3 +- .../ingredient/MMOItemIngredient.java | 3 +- .../ingredient/VanillaIngredient.java | 145 ++++++++++-- .../inventory/VanillaPlayerIngredient.java | 12 +- .../api/crafting/recipe/CraftingRecipe.java | 169 ++++++++++++-- .../event/MMOItemsProjectileFireEvent.java | 94 ++++++++ .../event/MMOItemsSpecialWeaponAttack.java | 48 ++++ .../interaction/weapon/untargeted/Musket.java | 24 ++ .../interaction/weapon/untargeted/Staff.java | 20 ++ .../weapon/untargeted/UntargetedWeapon.java | 10 + .../interaction/weapon/untargeted/Whip.java | 23 ++ .../api/item/build/ItemStackBuilder.java | 11 +- .../util/crafting/CraftingRecipeDisplay.java | 6 +- .../item/util/crafting/QueueItemDisplay.java | 2 +- .../mythicmobs/MythicMobsCompatibility.java | 19 ++ .../MMOItemsArrowVolleyMechanic.java | 187 ++++++++++++++++ .../mechanics/MMOItemsOnShootAura.java | 211 ++++++++++++++++++ .../mmoitems/gui/CraftingStationPreview.java | 6 +- .../mmoitems/gui/CraftingStationView.java | 2 +- .../mmoitems/gui/edition/AbilityEdition.java | 2 +- .../Indyuce/mmoitems/listener/ItemUse.java | 44 +++- .../mmoitems/manager/ConfigManager.java | 5 +- .../mmoitems/manager/EntityManager.java | 25 ++- .../net/Indyuce/mmoitems/stat/Abilities.java | 5 +- .../mmoitems/stat/data/AbilityData.java | 9 +- 30 files changed, 1023 insertions(+), 91 deletions(-) create mode 100644 src/main/java/net/Indyuce/mmoitems/api/event/MMOItemsProjectileFireEvent.java create mode 100644 src/main/java/net/Indyuce/mmoitems/api/event/MMOItemsSpecialWeaponAttack.java create mode 100644 src/main/java/net/Indyuce/mmoitems/comp/mythicmobs/mechanics/MMOItemsArrowVolleyMechanic.java create mode 100644 src/main/java/net/Indyuce/mmoitems/comp/mythicmobs/mechanics/MMOItemsOnShootAura.java diff --git a/pom.xml b/pom.xml index 4657acf2..41f9bd89 100644 --- a/pom.xml +++ b/pom.xml @@ -141,7 +141,7 @@ io.lumine MythicLib-dist - 1.3-R22-SNAPSHOT + 1.3-R25-SNAPSHOT provided diff --git a/src/main/java/net/Indyuce/mmoitems/MMOItems.java b/src/main/java/net/Indyuce/mmoitems/MMOItems.java index 65e1ab08..ea4d3a12 100644 --- a/src/main/java/net/Indyuce/mmoitems/MMOItems.java +++ b/src/main/java/net/Indyuce/mmoitems/MMOItems.java @@ -97,7 +97,7 @@ public class MMOItems extends LuminePlugin { private VaultSupport vaultSupport; private RPGHandler rpgPlugin; - private static final int MYTHICLIB_COMPATIBILITY_INDEX = 5; + private static final int MYTHICLIB_COMPATIBILITY_INDEX = 6; public MMOItems() { plugin = this; } @@ -809,6 +809,17 @@ public class MMOItems extends LuminePlugin { } } + /** + * JULES DO NOT DELETE THIS AGAIN I KNOW ITS UNUSED PRECISELY BECAUSE I ALWAYS COMMENT + * ALL ITS USAGES BEFORE PUSHING ANY UPDATES, I USE IT FOR SPAMMY DEVELOPER MESSAGES + * + * Note that {@link #print(Level, String, String, String...)} is used for actual warnings + * or such that the users may see, so dont delete that one either. + * + * @author Gunging + */ + public static void log(@Nullable String message, @NotNull String... replaces) { print(null, message, null, replaces); } + /** * @return The server's console sender. * @author Gunging diff --git a/src/main/java/net/Indyuce/mmoitems/MMOUtils.java b/src/main/java/net/Indyuce/mmoitems/MMOUtils.java index f6ee4681..154112de 100644 --- a/src/main/java/net/Indyuce/mmoitems/MMOUtils.java +++ b/src/main/java/net/Indyuce/mmoitems/MMOUtils.java @@ -59,7 +59,10 @@ public class MMOUtils { switch (name) { case "ON_HIT": return TriggerType.ATTACK; case "WHEN_HIT": return TriggerType.DAMAGED; - default: return TriggerType.valueOf(name); + default: + + TriggerType trigger = TriggerType.valueOf(name); + return trigger; } } diff --git a/src/main/java/net/Indyuce/mmoitems/api/crafting/ConfigMMOItem.java b/src/main/java/net/Indyuce/mmoitems/api/crafting/ConfigMMOItem.java index 3d0cdace..2e96de35 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/crafting/ConfigMMOItem.java +++ b/src/main/java/net/Indyuce/mmoitems/api/crafting/ConfigMMOItem.java @@ -10,6 +10,9 @@ import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate; import net.Indyuce.mmoitems.api.player.RPGPlayer; import org.jetbrains.annotations.NotNull; +/** + * Consider using {@link MMOItemUIFilter}, stores the same information but works more abstractly from MythicLib. + */ public class ConfigMMOItem { private final MMOItemTemplate template; private final int amount; @@ -58,7 +61,5 @@ public class ConfigMMOItem { return preview == null ? (preview = template.newBuilder(0, null).build().newBuilder().build(true)).clone() : preview.clone(); } - public int getAmount() { - return amount; - } + public int getAmount() { return amount; } } diff --git a/src/main/java/net/Indyuce/mmoitems/api/crafting/CraftingStation.java b/src/main/java/net/Indyuce/mmoitems/api/crafting/CraftingStation.java index c8985a13..9ea7447b 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/crafting/CraftingStation.java +++ b/src/main/java/net/Indyuce/mmoitems/api/crafting/CraftingStation.java @@ -151,7 +151,7 @@ public class CraftingStation extends PostLoadObject { * parameter because old files would be out of date, instead just looks for * a parameter of the crafting recipe which is 'output' */ - private Recipe loadRecipe(ConfigurationSection config) { + private Recipe loadRecipe(ConfigurationSection config) throws IllegalArgumentException { return config.contains("output") ? new CraftingRecipe(config) : new UpgradingRecipe(config); } } diff --git a/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/Ingredient.java b/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/Ingredient.java index 2ad1be01..63fd5863 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/Ingredient.java +++ b/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/Ingredient.java @@ -16,7 +16,7 @@ import org.jetbrains.annotations.NotNull; */ public abstract class Ingredient { private final String id; - private final int amount; + private int amount; public Ingredient(String id, MMOLineConfig config) { this(id, config.getInt("amount", 1)); @@ -35,6 +35,7 @@ public abstract class Ingredient { return id; } + public void setAmount(int amount) { this.amount = amount; } public int getAmount() { return amount; } diff --git a/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/MMOItemIngredient.java b/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/MMOItemIngredient.java index 46b4a35f..5b1aaa89 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/MMOItemIngredient.java +++ b/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/MMOItemIngredient.java @@ -22,8 +22,7 @@ import org.jetbrains.annotations.NotNull; public class MMOItemIngredient extends Ingredient { private final MMOItemTemplate template; - @NotNull - private final QuickNumberRange level; + @NotNull private final QuickNumberRange level; private final String display; public MMOItemIngredient(MMOLineConfig config) { diff --git a/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/VanillaIngredient.java b/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/VanillaIngredient.java index a97fd12e..b8084497 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/VanillaIngredient.java +++ b/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/VanillaIngredient.java @@ -1,63 +1,164 @@ package net.Indyuce.mmoitems.api.crafting.ingredient; import io.lumine.mythic.lib.api.MMOLineConfig; +import io.lumine.mythic.lib.api.crafting.uifilters.VanillaUIFilter; +import io.lumine.mythic.lib.api.crafting.uimanager.ProvidedUIFilter; +import io.lumine.mythic.lib.api.crafting.uimanager.UIFilterManager; import io.lumine.mythic.lib.api.item.NBTItem; import io.lumine.mythic.lib.api.util.LegacyComponent; +import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackCategory; +import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackMessage; +import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider; +import io.lumine.mythic.lib.api.util.ui.SilentNumbers; +import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.MMOUtils; import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.VanillaPlayerIngredient; import net.Indyuce.mmoitems.api.player.RPGPlayer; +import net.Indyuce.mmoitems.api.util.message.FFPMMOItems; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class VanillaIngredient extends Ingredient { - private final Material material; + @NotNull public ProvidedUIFilter getFilter() { return filter; } + @NotNull final ProvidedUIFilter filter; + + @NotNull public Material getMaterial() { return material; } + @NotNull final Material material; /** * displayName is the itemMeta display name; display corresponds to how the * ingredient displays in the crafting recipe GUI item lore */ - private final String displayName, display; + @NotNull final String display; + @Nullable final String displayName; + + /** + * Use vanilla stuff? + */ + boolean vanillaBackward = true; public VanillaIngredient(MMOLineConfig config) { super("vanilla", config); - config.validate("type"); + FriendlyFeedbackProvider ffp = new FriendlyFeedbackProvider(FFPMMOItems.get()); - material = Material.valueOf(config.getString("type").toUpperCase().replace("-", "_").replace(" ", "_")); + // Validate type I guess + config.validate("type"); + String itemFilter = config.getString("type", ""); + + //VING//MMOItems.log("\u00a78VING\u00a73 RD\u00a77 Reading\u00a7a " + itemFilter); + + // UIFilter or Vanilla Backwards Compatible? + if (itemFilter.contains(" ")) { + vanillaBackward = false; + //VING//MMOItems.log("\u00a78VING\u00a73 RD\u00a77 As Item Filter (yes)"); + + // Which item + ProvidedUIFilter sweetFilter = UIFilterManager.getUIFilter(itemFilter, ffp); + if (sweetFilter == null) { + + // Throw message + throw new IllegalArgumentException(SilentNumbers.collapseList(SilentNumbers.transcribeList(ffp.getFeedbackOf(FriendlyFeedbackCategory.ERROR), message -> { if (message instanceof FriendlyFeedbackMessage) { return ((FriendlyFeedbackMessage) message).forConsole(FFPMMOItems.get()); } return ""; }), "")); + } + + // Accepted as not-null + filter = sweetFilter; + + // Valid UIFilter? + if (!filter.isValid(ffp)) { + + // Throw message + throw new IllegalArgumentException(SilentNumbers.collapseList(SilentNumbers.transcribeList(ffp.getFeedbackOf(FriendlyFeedbackCategory.ERROR), message -> { if (message instanceof FriendlyFeedbackMessage) { return ((FriendlyFeedbackMessage) message).forConsole(FFPMMOItems.get()); } return ""; }), "")); + } + + // Consistent amounts + setAmount(filter.getAmount(getAmount())); + filter.setAmount(getAmount()); + + // Find the display name of the item + display = config.getString("display", findName()); + material = Material.STONE; + //VING//MMOItems.log("\u00a78VING\u00a73 RD\u00a77 Determined\u00a7e " + filter); + + } else { + //VING//MMOItems.log("\u00a78VING\u00a73 RD\u00a77 As Material (eh)"); + + // Parse material + material = Material.valueOf(itemFilter.toUpperCase().replace("-", "_").replace(" ", "_")); + + // Filter + filter = new ProvidedUIFilter(VanillaUIFilter.get(), material.toString(), "0"); + filter.setAmount(getAmount()); + + // Display is the name of the material, or whatever specified in the config. + display = config.getString("display", MMOUtils.caseOnWords(material.toString().toLowerCase().replace("_", " "))); + //VING//MMOItems.log("\u00a78VING\u00a73 RD\u00a77 Determined\u00a73 " + material.toString()); + + } + + // Valid UIFilter? + if (filter.getItemStack(null) == null) { + + // Throw message + throw new IllegalArgumentException(SilentNumbers.collapseList(SilentNumbers.transcribeList(ffp.getFeedbackOf(FriendlyFeedbackCategory.ERROR), message -> { if (message instanceof FriendlyFeedbackMessage) { return ((FriendlyFeedbackMessage) message).forConsole(FFPMMOItems.get()); } return ""; }), "")); + } + + // Display-Name, apparently displayName = config.contains("name") ? config.getString("name") : null; - display = config.contains("display") ? config.getString("display") : MMOUtils.caseOnWords(material.name().toLowerCase().replace("_", " ")); } - @Override - public String getKey() { - return "vanilla:" + material.name().toLowerCase() + "_" + displayName; - } + @Override public String getKey() { return "vanilla:" + (vanillaBackward ? material.toString().toLowerCase() : filter.toString().toLowerCase().replace(" ", "__")) + "_" + displayName; } - @Override - public String formatDisplay(String s) { - return s.replace("#item#", display).replace("#amount#", "" + getAmount()); - } + @Override public String formatDisplay(String s) { return s.replace("#item#", display).replace("#amount#", String.valueOf(getAmount())); } @Override public boolean matches(VanillaPlayerIngredient ing) { + //VING//MMOItems.log("\u00a78VING\u00a79 MCH\u00a77 Comparing given \u00a73 " + SilentNumbers.getItemName(ing.getSourceItem()) + " to expected\u00a79 " + filter); - // Check for material - if (ing.getType() != material) - return false; + if (vanillaBackward) { + // Check for material + if (ing.getType() != material) { + //VING//MMOItems.log("\u00a78VING\u00a79 MCH\u00a7c Not right material \u00a78(expected \u00a76" + material.toString() + "\u00a78)"); + return false; } - // Check for display name - return ing.getDisplayName() != null ? ing.getDisplayName().equals(displayName) : displayName == null; + //MMOItems.log("\u00a78VING\u00a79 MCH\u00a77 Display Name Ingredient:\u00a7a " + ing.getDisplayName()); + //MMOItems.log("\u00a78VING\u00a79 MCH\u00a77 Display Name Requested:\u00a7a " + displayName); + //MMOItems.log("\u00a78VING\u00a79 MCH\u00a77 Display Name Check:\u00a7a " + (ing.getDisplayName() != null ? ing.getDisplayName().equals(displayName) : displayName == null)); + + // Check for display name + return ing.getDisplayName() != null ? ing.getDisplayName().equals(displayName) : displayName == null; + + } else { + //VING//MMOItems.log("\u00a78VING\u00a79 MCH\u00a77 Poof Check \u00a73 " + filter + "\u00a77: \u00a7a " + (filter.matches(ing.getSourceItem(), true, null))); + + // Sweet PooF matching + return filter.matches(ing.getSourceItem(), true, null); + } } @NotNull @Override public ItemStack generateItemStack(@NotNull RPGPlayer player) { - NBTItem item = NBTItem.get(new ItemStack(material, getAmount())); - if (displayName != null) { - item.setDisplayNameComponent(LegacyComponent.parse(displayName)); - } + + // Stack + ItemStack stack = filter.getItemStack(null); + stack.setAmount(getAmount()); + + // Apparently get as NBT Item + NBTItem item = NBTItem.get(stack); + + // Then rename (okay) + if (displayName != null) { item.setDisplayNameComponent(LegacyComponent.parse(displayName)); } + + // Return return item.toItem(); } + + /** + * @return The name of the item the ProvidedUIFilter encodes for. + */ + @NotNull String findName() { return SilentNumbers.getItemName(filter.getParent().getDisplayStack(filter.getArgument(), filter.getData(), null), false); } } diff --git a/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/inventory/VanillaPlayerIngredient.java b/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/inventory/VanillaPlayerIngredient.java index eb19036f..f1f30759 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/inventory/VanillaPlayerIngredient.java +++ b/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/inventory/VanillaPlayerIngredient.java @@ -2,22 +2,28 @@ package net.Indyuce.mmoitems.api.crafting.ingredient.inventory; import io.lumine.mythic.lib.api.item.NBTItem; import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; public class VanillaPlayerIngredient extends PlayerIngredient { + @NotNull public ItemStack getSourceItem() { return sourceItem; } + @NotNull final ItemStack sourceItem; + private final Material material; - @Nullable - private final String displayName; + @Nullable private final String displayName; public VanillaPlayerIngredient(NBTItem item) { super(item); + // Restore item + sourceItem = item.toItem(); this.material = item.getItem().getType(); ItemMeta meta = item.getItem().getItemMeta(); - this.displayName = meta.hasDisplayName() ? meta.getDisplayName() : null; + if (meta != null) { this.displayName = meta.hasDisplayName() ? meta.getDisplayName() : null; } else { this.displayName = null; } } public Material getType() { diff --git a/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/CraftingRecipe.java b/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/CraftingRecipe.java index e15a6676..7ce8cbfe 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/CraftingRecipe.java +++ b/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/CraftingRecipe.java @@ -1,49 +1,184 @@ package net.Indyuce.mmoitems.api.crafting.recipe; +import io.lumine.mythic.lib.api.crafting.uimanager.ProvidedUIFilter; +import io.lumine.mythic.lib.api.crafting.uimanager.UIFilterManager; import io.lumine.mythic.lib.api.util.SmartGive; +import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackCategory; +import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackMessage; +import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider; +import io.lumine.mythic.lib.api.util.ui.SilentNumbers; +import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.api.crafting.ConfigMMOItem; import net.Indyuce.mmoitems.api.crafting.CraftingStation; import net.Indyuce.mmoitems.api.crafting.CraftingStatus.CraftingQueue; +import net.Indyuce.mmoitems.api.crafting.MMOItemUIFilter; import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.IngredientInventory; import net.Indyuce.mmoitems.api.event.PlayerUseCraftingStationEvent; +import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate; import net.Indyuce.mmoitems.api.item.util.ConfigItems; import net.Indyuce.mmoitems.api.player.PlayerData; +import net.Indyuce.mmoitems.api.player.RPGPlayer; +import net.Indyuce.mmoitems.api.util.message.FFPMMOItems; import net.Indyuce.mmoitems.api.util.message.Message; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Sound; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class CraftingRecipe extends Recipe { - private final ConfigMMOItem output; + @NotNull public static final String UNSPECIFIED = "N/A"; + + public CraftingRecipe(@NotNull ConfigurationSection config) throws IllegalArgumentException { + super(config); + + craftingTime = config.getDouble("crafting-time"); + + // Legacy loading + String uiFilter = config.getString("output.item", UNSPECIFIED); + String miType = config.getString("output.type", UNSPECIFIED).toUpperCase().replace("-", "_").replace(" ", "_"); + String miID = config.getString("output.id", UNSPECIFIED).toUpperCase().replace("-", "_").replace(" ", "_"); + + // Yes + FriendlyFeedbackProvider ffp = new FriendlyFeedbackProvider(FFPMMOItems.get()); + + // Both legacy specified? + if (!UNSPECIFIED.equals(miType) && !UNSPECIFIED.equals(miID)) { + + // Generate filter + ProvidedUIFilter sweetOutput = UIFilterManager.getUIFilter("m", miType, miID, config.getString("output.amount", "1"), ffp); + + // Is it null? + if (sweetOutput == null) { + + // Throw message + throw new IllegalArgumentException(SilentNumbers.collapseList(SilentNumbers.transcribeList(ffp.getFeedbackOf(FriendlyFeedbackCategory.ERROR), message -> { if (message instanceof FriendlyFeedbackMessage) { return ((FriendlyFeedbackMessage) message).forConsole(FFPMMOItems.get()); } return ""; }), "")); + } + + // Accept + output = sweetOutput; + + // New method specified? + } else if (!UNSPECIFIED.equals(uiFilter)) { + + // Generate filter + ProvidedUIFilter sweetOutput = UIFilterManager.getUIFilter(uiFilter, ffp); + + // Is it null? + if (sweetOutput == null) { + + // Throw message + throw new IllegalArgumentException(SilentNumbers.collapseList(SilentNumbers.transcribeList(ffp.getFeedbackOf(FriendlyFeedbackCategory.ERROR), message -> { if (message instanceof FriendlyFeedbackMessage) { return ((FriendlyFeedbackMessage) message).forConsole(FFPMMOItems.get()); } return ""; }), "")); + } + + // Accept + output = sweetOutput; + + // Invalid filter + } else { + + // Throw message + throw new IllegalArgumentException(FriendlyFeedbackProvider.quickForConsole(FFPMMOItems.get(), "Config must contain a valid Type and ID, or a valid UIFilter. ")); + } + + // Valid UIFilter? + if (!output.isValid(ffp)) { + + // Throw message + throw new IllegalArgumentException(SilentNumbers.collapseList(SilentNumbers.transcribeList(ffp.getFeedbackOf(FriendlyFeedbackCategory.ERROR), message -> { if (message instanceof FriendlyFeedbackMessage) { return ((FriendlyFeedbackMessage) message).forConsole(FFPMMOItems.get()); } return ""; }), "")); + } + + // Valid UIFilter? + if (output.getItemStack(ffp) == null) { + + // Throw message + throw new IllegalArgumentException(SilentNumbers.collapseList(SilentNumbers.transcribeList(ffp.getFeedbackOf(FriendlyFeedbackCategory.ERROR), message -> { if (message instanceof FriendlyFeedbackMessage) { return ((FriendlyFeedbackMessage) message).forConsole(FFPMMOItems.get()); } return ""; }), "")); + } + + // Its a MMOItem UIFilter, then? + if (output.getParent() instanceof MMOItemUIFilter) { + + // Find template + MMOItemTemplate template = MMOItems.plugin.getTemplates().getTemplate(MMOItems.plugin.getTypes().get(output.getArgument()), output.getData()); + + // Not possible tho + if (template == null) { + + // Throw message + throw new IllegalArgumentException(FriendlyFeedbackProvider.quickForConsole(FFPMMOItems.get(), "This should be impossible, please contact $egunging$b: $fThe ProvidedUIFilter was flagged as 'valid' but clearly is not. $enet.Indyuce.mmoitems.api.crafting.recipe$b. ")); + } + + // Identify MMOItems operation + identifiedMMO = new ConfigMMOItem(template, output.getAmount(1)); + } + } /* * There can't be any crafting time for upgrading recipes since there is no * way to save an MMOItem in the config file TODO save as ItemStack */ private final double craftingTime; + public double getCraftingTime() { return craftingTime; } + public boolean isInstant() { return craftingTime <= 0; } - public CraftingRecipe(ConfigurationSection config) { - super(config); + /** + * @return The item specified by the player that will be produced by this recipe. + */ + @NotNull public ProvidedUIFilter getOutput() { return output; } + @NotNull private final ProvidedUIFilter output; - craftingTime = config.getDouble("crafting-time"); + @Nullable ConfigMMOItem identifiedMMO; + /** + * @return The output ItemStack from this + */ + @SuppressWarnings("ConstantConditions") + @NotNull public ItemStack getOutputItemStack(@Nullable RPGPlayer rpg) { - // load recipe output - output = new ConfigMMOItem(config.getConfigurationSection("output")); + // Generate as MMOItem + if (identifiedMMO != null && rpg != null) { + + /* + * Generate in the legacy way. I do this way to preserve + * backwards compatibility, since this is how it used to + * be done. Don't want to break that without good reason. + */ + return identifiedMMO.generate(rpg); + } + + // Generate from ProvidedUIFilter, guaranteed to not be null don't listen to the inspection. + return output.getItemStack(null); } + /** + * @return The preview ItemStack from this + */ + @NotNull public ItemStack getPreviewItemStack() { - public double getCraftingTime() { - return craftingTime; - } + // Generate as MMOItem + if (identifiedMMO != null) { - public boolean isInstant() { - return craftingTime <= 0; - } + /* + * Generate in the legacy way. I do this way to preserve + * backwards compatibility, since this is how it used to + * be done. Don't want to break that without good reason. + */ + return identifiedMMO.getPreview(); + } - public ConfigMMOItem getOutput() { - return output; + // Generate from ProvidedUIFilter, guaranteed to not be null don't listen to the inspection. + //return output.getParent().getDisplayStack(output.getArgument(), output.getData(), null); + //return output.getDisplayStack(null); + ItemStack gen = output.getParent().getDisplayStack(output.getArgument(), output.getData(), null); + gen.setAmount(output.getAmount(1)); + ItemMeta itemMeta = gen.getItemMeta(); + if (itemMeta != null) { + itemMeta.setDisplayName(SilentNumbers.getItemName(gen, false) + "\u00a7\u02ab"); + gen.setItemMeta(itemMeta); } + return gen; } + public int getOutputAmount() { return output.getAmount(1); } @Override public boolean whenUsed(PlayerData data, IngredientInventory inv, CheckedRecipe recipe, CraftingStation station) { @@ -57,7 +192,7 @@ public class CraftingRecipe extends Recipe { */ if (isInstant()) { - ItemStack result = hasOption(RecipeOption.OUTPUT_ITEM) ? getOutput().generate(data.getRPG()) : null; + ItemStack result = hasOption(RecipeOption.OUTPUT_ITEM) ? getOutputItemStack(data.getRPG()) : null; PlayerUseCraftingStationEvent event = new PlayerUseCraftingStationEvent(data, station, recipe, result); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) @@ -113,9 +248,7 @@ public class CraftingRecipe extends Recipe { } @Override - public ItemStack display(CheckedRecipe recipe) { - return ConfigItems.CRAFTING_RECIPE_DISPLAY.newBuilder(recipe).build(); - } + public ItemStack display(CheckedRecipe recipe) { return ConfigItems.CRAFTING_RECIPE_DISPLAY.newBuilder(recipe).build(); } @Override public CheckedRecipe evaluateRecipe(PlayerData data, IngredientInventory inv) { diff --git a/src/main/java/net/Indyuce/mmoitems/api/event/MMOItemsProjectileFireEvent.java b/src/main/java/net/Indyuce/mmoitems/api/event/MMOItemsProjectileFireEvent.java new file mode 100644 index 00000000..eaca97e1 --- /dev/null +++ b/src/main/java/net/Indyuce/mmoitems/api/event/MMOItemsProjectileFireEvent.java @@ -0,0 +1,94 @@ +package net.Indyuce.mmoitems.api.event; + +import io.lumine.mythic.lib.api.item.NBTItem; +import io.lumine.mythic.lib.damage.DamageType; +import io.lumine.mythic.lib.player.PlayerMetadata; +import org.bukkit.entity.Entity; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.bukkit.event.entity.EntityShootBowEvent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Called when a MMOItems arrow or trident entity is fired. + *

+ * Contains information even on the temporary stats the player had when + * they executed the action, if that matters for some reason... + *

+ * Note that this event fires before the projectile actually registers, + * be mindful of changes made to the temporary stats because they will + * affect the projectile. + */ +public class MMOItemsProjectileFireEvent extends Event { + + /** + * @return The item the player used to fire this arrow. + */ + @NotNull public NBTItem getSourceItem() { return sourceItem; } + @NotNull NBTItem sourceItem; + /** + * Note that you must use {@link #setFinalDamage(double)} to modify the attack + * damage of the projectile rather than editing the attack damage of the player + * in here, as it will get overwritten. + * + * @return The stats the player had at the moment of firing the projectile. + */ + @NotNull public PlayerMetadata getPlayerStatsSnapshot() { return playerStatsSnapshot; } + @NotNull PlayerMetadata playerStatsSnapshot; + /** + * @return The projectile entity that was fired, arrow or trident. + */ + @NotNull public Entity getProjectile() { return projectile; } + @NotNull Entity projectile; + /** + * @return The damage this projectile will deal + */ + public double getFinalDamage() { return finalDamage; } + double finalDamage; + /** + * @param damage The damage this projectile will deal + */ + public void setFinalDamage(double damage) { finalDamage = damage; } + /** + * @return The original damage amount + */ + public double getDamageMultiplicator() { return damageMultiplicator; } + double damageMultiplicator; + /** + * @return The original damage amount + */ + public double getOriginalDamage() { return originalDamage; } + double originalDamage; + /** + * @return The kinds of damage this projectile will deal, what it will scale with. + */ + @NotNull public DamageType[] getDamageTypes() { return damageTypes; } + @NotNull DamageType[] damageTypes = { DamageType.PROJECTILE, DamageType.PHYSICAL, DamageType.WEAPON }; + /** + * @param types The kinds of damage this projectile will deal, what it will scale with. + */ + public void setDamageTypes(@NotNull DamageType[] types) { damageTypes = types; } + /** + * @return The event that caused this projectile to be fired. Honestly, only for informational purposes of whatever listening API. + */ + @Nullable public EntityShootBowEvent getEvent() { return event; } + @Nullable EntityShootBowEvent event; + + public MMOItemsProjectileFireEvent(@NotNull PlayerMetadata player, @NotNull Entity projectile, @NotNull NBTItem item, @Nullable EntityShootBowEvent event, double originalDamage, double damageMultiplicator) { + playerStatsSnapshot = player; + this.projectile = projectile; + sourceItem = item; + this.originalDamage = originalDamage; + this.damageMultiplicator= damageMultiplicator; + finalDamage = originalDamage * damageMultiplicator; + this.event = event; + } + + //region Event Standard + private static final HandlerList handlers = new HandlerList(); + @NotNull + @Override public HandlerList getHandlers() { return handlers; } + public static HandlerList getHandlerList() { return handlers; } + //endregion +} diff --git a/src/main/java/net/Indyuce/mmoitems/api/event/MMOItemsSpecialWeaponAttack.java b/src/main/java/net/Indyuce/mmoitems/api/event/MMOItemsSpecialWeaponAttack.java new file mode 100644 index 00000000..1c512df4 --- /dev/null +++ b/src/main/java/net/Indyuce/mmoitems/api/event/MMOItemsSpecialWeaponAttack.java @@ -0,0 +1,48 @@ +package net.Indyuce.mmoitems.api.event; + +import net.Indyuce.mmoitems.api.interaction.UseItem; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class MMOItemsSpecialWeaponAttack extends PlayerEvent implements Cancellable { + + /** + * @return The weapon used by the player that fired this event + */ + @NotNull public UseItem getWeapon() { return weapon; } + @NotNull final UseItem weapon; + + /** + * @return The entity that is being hit by this special attack + */ + @Nullable public LivingEntity getTarget() { return target; } + @Nullable final LivingEntity target; + + public MMOItemsSpecialWeaponAttack(@NotNull Player who, @NotNull UseItem weapon, @Nullable LivingEntity target) { + super(who); + this.weapon = weapon; + this.target = target; + } + + //region Event Standard + private static final HandlerList handlers = new HandlerList(); + @NotNull @Override public HandlerList getHandlers() { return handlers; } + public static HandlerList getHandlerList() { return handlers; } + //endregion + + //region Cancellable Standard + boolean cancelled; + @Override public boolean isCancelled() { + return cancelled; + } + @Override public void setCancelled(boolean cancel) { + cancelled = cancel; + } + //endregion +} diff --git a/src/main/java/net/Indyuce/mmoitems/api/interaction/weapon/untargeted/Musket.java b/src/main/java/net/Indyuce/mmoitems/api/interaction/weapon/untargeted/Musket.java index 6dd842bf..84a58f05 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/interaction/weapon/untargeted/Musket.java +++ b/src/main/java/net/Indyuce/mmoitems/api/interaction/weapon/untargeted/Musket.java @@ -17,6 +17,7 @@ import net.Indyuce.mmoitems.api.player.PlayerData.CooldownType; import org.bukkit.Color; import org.bukkit.Location; import org.bukkit.Sound; +import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.util.Vector; @@ -67,4 +68,27 @@ public class Musket extends UntargetedWeapon { trace.draw(loc, vec, 2, Color.BLACK); getPlayer().getWorld().playSound(getPlayer().getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, 2, 2); } + + @Override + public LivingEntity untargetedTargetTrace(EquipmentSlot slot) { + + // Temporary Stats + PlayerMetadata stats = getPlayerData().getStats().newTemporary(slot); + + // Range + double range = getValue(getNBTItem().getStat(ItemStats.RANGE.getId()), MMOItems.plugin.getConfig().getDouble("default.range")); + double a = Math.toRadians(getPlayer().getEyeLocation().getYaw() + 160); + Location loc = getPlayer().getEyeLocation().add(new Vector(Math.cos(a), 0, Math.sin(a)).multiply(.5)); + Vector vec = loc.getDirection(); + + // Raytrace + MMORayTraceResult trace = MythicLib.plugin.getVersion().getWrapper().rayTrace(stats.getPlayer(), vec, range, + entity -> MMOUtils.canTarget(stats.getPlayer(), entity, InteractionType.OFFENSE_ACTION)); + + // Find entity + if (trace.hasHit()) { return trace.getHit(); } + + // No + return null; + } } diff --git a/src/main/java/net/Indyuce/mmoitems/api/interaction/weapon/untargeted/Staff.java b/src/main/java/net/Indyuce/mmoitems/api/interaction/weapon/untargeted/Staff.java index 630a43c4..5916b8be 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/interaction/weapon/untargeted/Staff.java +++ b/src/main/java/net/Indyuce/mmoitems/api/interaction/weapon/untargeted/Staff.java @@ -69,6 +69,26 @@ public class Staff extends UntargetedWeapon { } + @Override + public LivingEntity untargetedTargetTrace(EquipmentSlot slot) { + + // Temporary Stats + PlayerMetadata stats = getPlayerData().getStats().newTemporary(slot); + + // Range + double range = getValue(getNBTItem().getStat(ItemStats.RANGE.getId()), MMOItems.plugin.getConfig().getDouble("default.range")); + + // Raytrace + MMORayTraceResult trace = MythicLib.plugin.getVersion().getWrapper().rayTrace(stats.getPlayer(), range, + entity -> MMOUtils.canTarget(stats.getPlayer(), entity, InteractionType.OFFENSE_ACTION)); + + // Find Entity + if (trace.hasHit()) { return trace.getHit(); } + + // Nothing Found + return null; + } + public void specialAttack(LivingEntity target) { if (!MMOItems.plugin.getConfig().getBoolean("item-ability.staff.enabled")) return; diff --git a/src/main/java/net/Indyuce/mmoitems/api/interaction/weapon/untargeted/UntargetedWeapon.java b/src/main/java/net/Indyuce/mmoitems/api/interaction/weapon/untargeted/UntargetedWeapon.java index 2527b7c9..51ece6bb 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/interaction/weapon/untargeted/UntargetedWeapon.java +++ b/src/main/java/net/Indyuce/mmoitems/api/interaction/weapon/untargeted/UntargetedWeapon.java @@ -1,11 +1,13 @@ package net.Indyuce.mmoitems.api.interaction.weapon.untargeted; import io.lumine.mythic.lib.api.player.EquipmentSlot; +import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.block.Action; import net.Indyuce.mmoitems.api.interaction.weapon.Weapon; import io.lumine.mythic.lib.api.item.NBTItem; +import org.jetbrains.annotations.Nullable; public abstract class UntargetedWeapon extends Weapon { protected final WeaponType weaponType; @@ -24,6 +26,14 @@ public abstract class UntargetedWeapon extends Weapon { */ public abstract void untargetedAttack(EquipmentSlot slot); + /** + * Predicts which target will be get hit when using {@link #untargetedAttack(EquipmentSlot)} + * with the same exact information, this way the target is available beforehand. + * + * @param slot Slot being interacted with + */ + @Nullable public LivingEntity untargetedTargetTrace(EquipmentSlot slot) { return null; } + public WeaponType getWeaponType() { return weaponType; } diff --git a/src/main/java/net/Indyuce/mmoitems/api/interaction/weapon/untargeted/Whip.java b/src/main/java/net/Indyuce/mmoitems/api/interaction/weapon/untargeted/Whip.java index 51ba9437..7980a1f6 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/interaction/weapon/untargeted/Whip.java +++ b/src/main/java/net/Indyuce/mmoitems/api/interaction/weapon/untargeted/Whip.java @@ -17,6 +17,7 @@ import net.Indyuce.mmoitems.api.interaction.util.UntargetedDurabilityItem; import net.Indyuce.mmoitems.api.player.PlayerData.CooldownType; import org.bukkit.Location; import org.bukkit.Particle; +import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.util.Vector; @@ -54,4 +55,26 @@ public class Whip extends UntargetedWeapon { (tick) -> tick.getWorld().spawnParticle(Particle.CRIT, tick, 0, .1, .1, .1, 0)); getPlayer().getWorld().playSound(getPlayer().getLocation(), VersionSound.ENTITY_FIREWORK_ROCKET_BLAST.toSound(), 1, 2); } + + @Override + public LivingEntity untargetedTargetTrace(EquipmentSlot slot) { + + // Temporary Stats + PlayerMetadata stats = getPlayerData().getStats().newTemporary(slot); + + // Range + double range = getValue(getNBTItem().getStat(ItemStats.RANGE.getId()), MMOItems.plugin.getConfig().getDouble("default.range")); + double a = Math.toRadians(getPlayer().getEyeLocation().getYaw() + 160); + Location loc = getPlayer().getEyeLocation().add(new Vector(Math.cos(a), 0, Math.sin(a)).multiply(.5)); + + // Ray Trace + MMORayTraceResult trace = MythicLib.plugin.getVersion().getWrapper().rayTrace(stats.getPlayer(), range, + entity -> MMOUtils.canTarget(stats.getPlayer(), entity, InteractionType.OFFENSE_ACTION)); + + // Find entity + if (trace.hasHit()) { return trace.getHit(); } + + // No + return null; + } } diff --git a/src/main/java/net/Indyuce/mmoitems/api/item/build/ItemStackBuilder.java b/src/main/java/net/Indyuce/mmoitems/api/item/build/ItemStackBuilder.java index 5624e244..7eed10e2 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/item/build/ItemStackBuilder.java +++ b/src/main/java/net/Indyuce/mmoitems/api/item/build/ItemStackBuilder.java @@ -178,9 +178,7 @@ public class ItemStackBuilder { // Get Template MMOItemTemplate template = MMOItems.plugin.getTemplates().getTemplate(builtMMOItem.getType(), builtMMOItem.getId()); - if (template == null) { - throw new IllegalArgumentException("MMOItem $r" + builtMMOItem.getType().getId() + " " + builtMMOItem.getId() + "$b doesn't exist."); - } + if (template == null) { throw new IllegalArgumentException("MMOItem $r" + builtMMOItem.getType().getId() + " " + builtMMOItem.getId() + "$b doesn't exist."); } // Make necessary lore changes ((Previewable) stat).whenPreviewed(this, builtMMOItem.getData(stat), template.getBaseItemData().get(stat)); @@ -195,11 +193,8 @@ public class ItemStackBuilder { } catch (IllegalArgumentException | NullPointerException exception) { // That - MMOItems.plugin.getLogger().log(Level.WARNING, FriendlyFeedbackProvider.quickForConsole(FFPMMOItems.get(), - "An error occurred while trying to generate item '$f{0}$b' with stat '$f{1}$b': {2}", - builtMMOItem.getId(), - stat.getId(), - exception.getMessage())); + MMOItems.print(Level.WARNING, "An error occurred while trying to generate item '$f{0}$b' with stat '$f{1}$b': {2}", + "ItemStackBuilder", builtMMOItem.getId(), stat.getId(), exception.getMessage()); } // Display gem stone lore hint thing diff --git a/src/main/java/net/Indyuce/mmoitems/api/item/util/crafting/CraftingRecipeDisplay.java b/src/main/java/net/Indyuce/mmoitems/api/item/util/crafting/CraftingRecipeDisplay.java index e1414649..4d8c8643 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/item/util/crafting/CraftingRecipeDisplay.java +++ b/src/main/java/net/Indyuce/mmoitems/api/item/util/crafting/CraftingRecipeDisplay.java @@ -109,11 +109,11 @@ public class CraftingRecipeDisplay extends ConfigItem { lore.add(conditionsIndex++, condition.format()); } - ItemStack item = craftingRecipe.getOutput().getPreview(); - int amount = craftingRecipe.getOutput().getAmount(); + ItemStack item = craftingRecipe.getPreviewItemStack(); + int amount = craftingRecipe.getOutputAmount(); if (amount > 64) - lore.add(0, Message.STATION_BIG_STACK.format(ChatColor.GOLD, "#size#", "" + amount).toString()); + lore.add(0, Message.STATION_BIG_STACK.format(ChatColor.GOLD, "#size#", String.valueOf(amount)).toString()); else item.setAmount(amount); diff --git a/src/main/java/net/Indyuce/mmoitems/api/item/util/crafting/QueueItemDisplay.java b/src/main/java/net/Indyuce/mmoitems/api/item/util/crafting/QueueItemDisplay.java index 46930aea..8c83ccd3 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/item/util/crafting/QueueItemDisplay.java +++ b/src/main/java/net/Indyuce/mmoitems/api/item/util/crafting/QueueItemDisplay.java @@ -75,7 +75,7 @@ public class QueueItemDisplay extends ConfigItem { for (String key : replace.keySet()) lore.set(lore.indexOf(key), replace.get(key).replace("#left#", formatDelay(crafting.getLeft()))); - ItemStack item = crafting.getRecipe().getOutput().getPreview(); + ItemStack item = crafting.getRecipe().getPreviewItemStack(); item.setAmount(position); ItemMeta meta = item.getItemMeta(); meta.addItemFlags(ItemFlag.values()); diff --git a/src/main/java/net/Indyuce/mmoitems/comp/mythicmobs/MythicMobsCompatibility.java b/src/main/java/net/Indyuce/mmoitems/comp/mythicmobs/MythicMobsCompatibility.java index 1e7d04b8..755fc426 100644 --- a/src/main/java/net/Indyuce/mmoitems/comp/mythicmobs/MythicMobsCompatibility.java +++ b/src/main/java/net/Indyuce/mmoitems/comp/mythicmobs/MythicMobsCompatibility.java @@ -1,11 +1,14 @@ package net.Indyuce.mmoitems.comp.mythicmobs; import io.lumine.xikage.mythicmobs.MythicMobs; +import io.lumine.xikage.mythicmobs.api.bukkit.events.MythicMechanicLoadEvent; import io.lumine.xikage.mythicmobs.api.bukkit.events.MythicReloadedEvent; import io.lumine.xikage.mythicmobs.mobs.MythicMob; import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem; import net.Indyuce.mmoitems.api.player.PlayerData; +import net.Indyuce.mmoitems.comp.mythicmobs.mechanics.MMOItemsArrowVolleyMechanic; +import net.Indyuce.mmoitems.comp.mythicmobs.mechanics.MMOItemsOnShootAura; import net.Indyuce.mmoitems.comp.mythicmobs.stat.FactionDamage; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -29,6 +32,22 @@ public class MythicMobsCompatibility implements Listener { Bukkit.getPluginManager().registerEvents(this, MMOItems.plugin); } + + @EventHandler(priority = EventPriority.HIGH) + public void b(MythicMechanicLoadEvent event) { + + // Switch Mechanic ig + switch (event.getMechanicName().toLowerCase()) { + case "mmoitemsvolley": + event.register(new MMOItemsArrowVolleyMechanic(event.getContainer().getConfigLine(), event.getConfig())); + break; + case "onmmoitemuse": + event.register(new MMOItemsOnShootAura(event.getContainer().getConfigLine(), event.getConfig())); + break; + default: break; + } + } + /** * MythicLib skill handlers are reloaded on priority {@link EventPriority#NORMAL} * MMOCore and MMOItems use HIGH or HIGHEST diff --git a/src/main/java/net/Indyuce/mmoitems/comp/mythicmobs/mechanics/MMOItemsArrowVolleyMechanic.java b/src/main/java/net/Indyuce/mmoitems/comp/mythicmobs/mechanics/MMOItemsArrowVolleyMechanic.java new file mode 100644 index 00000000..4ce616f8 --- /dev/null +++ b/src/main/java/net/Indyuce/mmoitems/comp/mythicmobs/mechanics/MMOItemsArrowVolleyMechanic.java @@ -0,0 +1,187 @@ +package net.Indyuce.mmoitems.comp.mythicmobs.mechanics; + +import io.lumine.mythic.lib.api.crafting.uimanager.ProvidedUIFilter; +import io.lumine.mythic.lib.api.crafting.uimanager.UIFilterManager; +import io.lumine.xikage.mythicmobs.adapters.AbstractEntity; +import io.lumine.xikage.mythicmobs.adapters.AbstractLocation; +import io.lumine.xikage.mythicmobs.adapters.SkillAdapter; +import io.lumine.xikage.mythicmobs.adapters.bukkit.BukkitAdapter; +import io.lumine.xikage.mythicmobs.io.MythicLineConfig; +import io.lumine.xikage.mythicmobs.skills.*; +import io.lumine.xikage.mythicmobs.skills.placeholders.parsers.PlaceholderDouble; +import io.lumine.xikage.mythicmobs.skills.placeholders.parsers.PlaceholderFloat; +import io.lumine.xikage.mythicmobs.skills.placeholders.parsers.PlaceholderInt; +import net.Indyuce.mmoitems.MMOItems; +import net.Indyuce.mmoitems.listener.ItemUse; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.EntityShootBowEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; + +/** + * An arrow volley method but uses the stats of the bow + * held by the player that is casting this skill, or at + * least a scaled version of them! + * + * @author Gunging + */ +public class MMOItemsArrowVolleyMechanic extends SkillMechanic implements ITargetedEntitySkill, ITargetedLocationSkill { + + @NotNull PlaceholderInt amount, spread, fireTicks, removeDelay; + @NotNull PlaceholderFloat velocity, scale; + @NotNull PlaceholderDouble xOffset, yOffset, zOffset, fOffset, sOffset; + + @Nullable ItemStack arrowItem; + boolean fullEvent; + boolean scalePerArrow; + boolean fromOrigin; + + public MMOItemsArrowVolleyMechanic(String line, MythicLineConfig mlc) { + super(line, mlc); + threadSafetyLevel = ThreadSafetyLevel.SYNC_ONLY; + + amount = mlc.getPlaceholderInteger(new String[] {"amount", "arrows", "a"}, 20); + spread = mlc.getPlaceholderInteger(new String[] {"spread", "s"}, 45); + fireTicks = mlc.getPlaceholderInteger(new String[] {"fireticks", "ft", "f"}, 0); + removeDelay = mlc.getPlaceholderInteger(new String[] {"removedelay", "rd", "r"}, 200); + velocity = mlc.getPlaceholderFloat(new String[] {"velocity", "v"}, 20); + scale = mlc.getPlaceholderFloat(new String[] {"statsscale", "ss"}, 1); + + fullEvent = mlc.getBoolean(new String[] {"fullevent", "fe"}, false); + scalePerArrow = mlc.getBoolean(new String[] {"scaleperarrow", "spa"}, false); + fromOrigin = mlc.getBoolean(new String[] {"fromorigin", "fo"}, false); + + //region Get Arrow Item + String itemFilter = mlc.getString(new String[] {"arrowitem", "item", "ai"}, null); + if (itemFilter != null) { + ProvidedUIFilter uiFilter = UIFilterManager.getUIFilter(itemFilter, null); + if (uiFilter != null) { + if (uiFilter.isValid(null) && uiFilter.getParent().fullyDefinesItem()) { + uiFilter.setAmount(1); + + // Generate Item + arrowItem = uiFilter.getItemStack(null); + } + } + } + //endregion + + // Offsets + xOffset = mlc.getPlaceholderDouble(new String[] {"startxoffset", "sxo"}, 0); + yOffset = mlc.getPlaceholderDouble(new String[] {"startyoffset", "syo"}, 3); + zOffset = mlc.getPlaceholderDouble(new String[] {"startzoffset", "szo"}, 0); + fOffset = mlc.getPlaceholderDouble(new String[] {"startfoffset", "sfo"}, 0); + sOffset = mlc.getPlaceholderDouble(new String[] {"startsoffset", "sso"}, 0); + } + + + @Override + public boolean castAtLocation(SkillMetadata data, AbstractLocation target) { + + // Caster must be a player + if (data.getCaster().getEntity().getBukkitEntity() instanceof Player) { + + // MMOItems Volley! + executeMIVolley(data.getCaster(), data, target, amount.get(data), velocity.get(data) * 0.1F, spread.get(data), fireTicks.get(data), removeDelay.get(data), scale); + + } else { + + // Run as normal mythicmobs arrow volley + SkillAdapter.get().executeVolley(data.getCaster(), target, amount.get(data), velocity.get(data) * 0.1F, spread.get(data), fireTicks.get(data), removeDelay.get(data)); + } + return true; + } + + @Override + public boolean castAtEntity(SkillMetadata data, AbstractEntity target) { + + // Caster must be a player + if (data.getCaster().getEntity().getBukkitEntity() instanceof Player) { + + // MMOItems Volley! + executeMIVolley(data.getCaster(), data, target.getLocation(), amount.get(data,target), velocity.get(data) * 0.1F, spread.get(data), fireTicks.get(data), removeDelay.get(data), scale); + + } else { + + // Run as normal mythicmobs arrow volley + SkillAdapter.get().executeVolley(data.getCaster(), target.getLocation(), amount.get(data,target), velocity.get(data) * 0.1F, spread.get(data), fireTicks.get(data), removeDelay.get(data)); + } + return true; + } + + + public void executeMIVolley(@NotNull SkillCaster caster, @NotNull SkillMetadata data, @NotNull AbstractLocation t, int amount, float velocity, float spread, int fireTicks, int removeDelay, @NotNull PlaceholderFloat statsMultiplier) { + + // Skill caster MUST be a player + if (!(caster.getEntity().getBukkitEntity() instanceof Player)) { return; } + Player player = (Player) caster.getEntity().getBukkitEntity(); + + // Target yeah + Location target = BukkitAdapter.adapt(t); + Location spawn = BukkitAdapter.adapt(fromOrigin ? data.getOrigin() : caster.getLocation()).clone(); + + //region Calculate Offsets + Vector forward = spawn.getDirection().normalize(); + Vector side = (new Vector(-forward.getZ(), 0.00001, forward.getX())).normalize(); + + double fS = fOffset.get(data); + double sS = sOffset.get(data); + + spawn.setX(spawn.getX() + xOffset.get(data) + (forward.getX() * fS) + (side.getX() * sS)); + spawn.setY(spawn.getY() + yOffset.get(data) + (forward.getY() * fS) + (side.getY() * sS)); + spawn.setZ(spawn.getZ() + zOffset.get(data) + (forward.getZ() * fS) + (side.getZ() * sS)); + //endregion + + // Direction vector + Vector v = target.toVector().subtract(spawn.toVector()).normalize(); + + // Player bow item is held?? + ItemStack bowItem = player.getInventory().getItemInMainHand().clone(); + ItemStack localArrowItem = (arrowItem != null ? arrowItem.clone() : new ItemStack(Material.ARROW)); + ItemUse use = new ItemUse(); + + // Parse + float arrowForce = statsMultiplier.get(data); + + // Spawn arrows + ArrayList arrowList = new ArrayList<>(); + for (int i = 0; i < amount; i++) { + + // Spawn Arrow + Arrow a = player.getWorld().spawnArrow(spawn, v, velocity, (spread/10.0F)); + a.setVelocity(a.getVelocity()); + + // Identify arrow as the player's + a.setShooter(player); + + // Run Event + EntityShootBowEvent shootBowEvent = new EntityShootBowEvent(player, bowItem, localArrowItem, a, EquipmentSlot.HAND, arrowForce, false); + if (fullEvent) { Bukkit.getPluginManager().callEvent(shootBowEvent); } else { use.handleCustomBows(shootBowEvent); } + + // Cancelled??? + if (shootBowEvent.isCancelled()) { a.remove(); continue; } + + // Set on fire I guess + if(fireTicks > 0) { a.setFireTicks(fireTicks); } + + // Add to list + arrowList.add(a); + + // Recalculate + if (scalePerArrow) { arrowForce = statsMultiplier.get(data); } + } + + // Remove after delay + Bukkit.getScheduler().scheduleSyncDelayedTask(MMOItems.plugin, () -> { + for (Arrow a : arrowList) { a.remove(); }arrowList.clear(); }, removeDelay); + } +} diff --git a/src/main/java/net/Indyuce/mmoitems/comp/mythicmobs/mechanics/MMOItemsOnShootAura.java b/src/main/java/net/Indyuce/mmoitems/comp/mythicmobs/mechanics/MMOItemsOnShootAura.java new file mode 100644 index 00000000..28d237ae --- /dev/null +++ b/src/main/java/net/Indyuce/mmoitems/comp/mythicmobs/mechanics/MMOItemsOnShootAura.java @@ -0,0 +1,211 @@ +package net.Indyuce.mmoitems.comp.mythicmobs.mechanics; + +import io.lumine.xikage.mythicmobs.MythicMobs; +import io.lumine.xikage.mythicmobs.adapters.AbstractEntity; +import io.lumine.xikage.mythicmobs.adapters.bukkit.BukkitAdapter; +import io.lumine.xikage.mythicmobs.io.MythicLineConfig; +import io.lumine.xikage.mythicmobs.mobs.GenericCaster; +import io.lumine.xikage.mythicmobs.skills.*; +import io.lumine.xikage.mythicmobs.skills.auras.Aura; +import io.lumine.xikage.mythicmobs.skills.placeholders.parsers.PlaceholderString; +import io.lumine.xikage.mythicmobs.utils.Events; +import net.Indyuce.mmoitems.MMOItems; +import net.Indyuce.mmoitems.api.event.MMOItemsSpecialWeaponAttack; +import net.Indyuce.mmoitems.api.interaction.weapon.Gauntlet; +import net.Indyuce.mmoitems.api.interaction.weapon.untargeted.*; +import org.bukkit.scheduler.BukkitRunnable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Optional; + +/** + * Sure there is the 'onShoot' aura for bows, but what about + * musket and crossbow and lute and...? + * + * This Aura will cover all of those. + * + * @author Gunging + */ +public class MMOItemsOnShootAura extends Aura implements ITargetedEntitySkill { + @NotNull PlaceholderString skillName; + @NotNull String weaponTypes; + @Nullable Skill metaskill; + boolean cancelEvent; + boolean forceAsPower; + + @NotNull final ArrayList auraWeapons = new ArrayList<>(); + + public MMOItemsOnShootAura(String skill, MythicLineConfig mlc) { + super(skill, mlc); + skillName = mlc.getPlaceholderString(new String[]{"skill", "s", "ondamagedskill", "ondamaged", "od", "onhitskill", "onhit", "oh", "meta", "m", "mechanics", "$", "()"}, "skill not found"); + weaponTypes = mlc.getString(new String[]{"weapons", "weapon", "w"}, "MUSKET"); + metaskill = GetSkill(skillName.get()); + cancelEvent = mlc.getBoolean(new String[]{"cancelevent", "ce"}, false); + + // Parse weapon types + ArrayList weaponTypesUnparsed = new ArrayList<>(); + if (weaponTypes.contains(",")) { weaponTypesUnparsed.addAll(Arrays.asList(weaponTypes.split(","))); } else { weaponTypesUnparsed.add(weaponTypes); } + + for (String weapon : weaponTypesUnparsed) { + // Try to get + try { + + // Parse + UseItemTypes weap = UseItemTypes.valueOf(weapon.toUpperCase()); + + // Yes + auraWeapons.add(weap); + + } catch (IllegalArgumentException ignored) {} + } + + // Attempt to fix meta skill + if (metaskill == null) { + //MM//OotilityCeption.Log("\u00a7c--->> \u00a7eMeta Skill Failure \u00a7c<<---"); + + // Try again i guess? + (new BukkitRunnable() { + public void run() { + + // Run Async + metaskill = GetSkill(skillName.get()); + + } + }).runTaskLater(MMOItems.plugin, 1L); + } + } + + public boolean castAtEntity(SkillMetadata data, AbstractEntity target) { + // Find caster + SkillCaster caster; + + // Will be caster of the skill, as a mythicmob + if (MythicMobs.inst().getMobManager().isActiveMob(target)) { + //SOM//OotilityCeption.Log("\u00a73 * \u00a77Target as ActiveMob"); + + // Just pull the mythicmob + caster = MythicMobs.inst().getMobManager().getMythicMobInstance(target); + + // If its a player or some other non-mythicmob + } else { + //SOM//OotilityCeption.Log("\u00a73 * \u00a77Target as Non MM"); + + // I guess make a new caster out of them + caster = new GenericCaster(target); + } + + new MMOItemsOnShootAura.Tracker(caster, data, target); + return true; + } + + private class Tracker extends AuraTracker implements IParentSkill, Runnable { + public Tracker(SkillCaster caster, SkillMetadata data, AbstractEntity entity) { + super(caster, entity, data); + this.start(); + } + + public void auraStart() { + this.registerAuraComponent(Events.subscribe(MMOItemsSpecialWeaponAttack.class).filter((event) -> { + + //SOM//OotilityCeption.Log("\u00a7cStep 3 \u00a77Subscribe Run: " + getName(event.getEntity()) + "\u00a77 vs " + getName(this.entity.get()) + "\u00a78 ~\u00a7e " + event.getEntity().getUniqueId().equals(this.entity.get().getUniqueId())); + + // Player is the one who has the aura applied, right? + if (!event.getPlayer().getUniqueId().equals(this.entity.get().getUniqueId())) { return false; } + + // All custom weapons fire it if none specified. + if (auraWeapons.size() == 0) { return true; } + + // Okay go through all weapon types, must match one + for (UseItemTypes weap : auraWeapons) { + if (weap.getInst().isInstance(event.getWeapon())) { return true; } } + + // None matched + return false; + + }).handler((event) -> { + + // Clone metadata + SkillMetadata meta = this.skillMetadata.deepClone(); + + // Refresh + if (metaskill == null) { metaskill = GetSkill(skillName.get(meta, meta.getCaster().getEntity())); } + + // Target obviously the projectile + AbstractEntity target = BukkitAdapter.adapt(event.getTarget()); + meta.setTrigger(target); + + //SOM//OotilityCeption.Log("\u00a7cStep 4 \u00a77Aura Run:\u00a7d " + logSkillData(meta) + "\u00a7b " + metaskill.getInternalName()); + if (this.executeAuraSkill(Optional.ofNullable(metaskill), meta)) { + + this.consumeCharge(); + + if (cancelEvent) { event.setCancelled(true); } + } + + })); + this.executeAuraSkill(MMOItemsOnShootAura.this.onStartSkill, this.skillMetadata); + } + } + + @Nullable public static Skill GetSkill(String skillName) { + + if (SkillExists(skillName)) { + + Optional mSkillFk = MythicMobs.inst().getSkillManager().getSkill(skillName); + if (mSkillFk == null) { return null; } + if (mSkillFk.isPresent()) { return mSkillFk.get(); } + } + + return null; + } + public static boolean SkillExists(String skillName) { + // If null no + if (skillName == null) { return false; } + + Optional mSkillFk = MythicMobs.inst().getSkillManager().getSkill(skillName); + + // Is there a skill of that name? + if (mSkillFk.isPresent()) { + + try { + // Ok then retrieve the skill + Skill mSkill = (Skill) mSkillFk.get(); + + // Success + return true; + + } catch (Exception e) { + + // RIP + return false; + } + + // The skill was not found + } else { + + // False means the skill does not exist. + return false; + } + } + + enum UseItemTypes { + CROSSBOW(Crossbow.class), + GAUNTLET(Gauntlet.class), + LUTE(Lute.class), + MUSKET(Musket.class), + STAFF(Staff.class), + WHIP(Whip.class); + + /** + * @return Class to use InstanceOf and identify a weapon. + */ + @NotNull public Class getInst() { return inst; } + @NotNull final Class inst; + UseItemTypes(@NotNull Class inst) { + this.inst = inst; + } + } +} diff --git a/src/main/java/net/Indyuce/mmoitems/gui/CraftingStationPreview.java b/src/main/java/net/Indyuce/mmoitems/gui/CraftingStationPreview.java index fe2c5105..ced495df 100644 --- a/src/main/java/net/Indyuce/mmoitems/gui/CraftingStationPreview.java +++ b/src/main/java/net/Indyuce/mmoitems/gui/CraftingStationPreview.java @@ -20,6 +20,7 @@ import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; @@ -40,6 +41,7 @@ public class CraftingStationPreview extends PluginInventory { this.recipe = recipe; } + @NotNull @Override public Inventory getInventory() { @@ -105,8 +107,8 @@ public class CraftingStationPreview extends PluginInventory { inv.setItem(slot, ConfigItems.FILL.getItem()); if (recipe.getRecipe() instanceof CraftingRecipe) { - ItemStack item = ((CraftingRecipe) recipe.getRecipe()).getOutput().getPreview(); - item.setAmount(((CraftingRecipe) recipe.getRecipe()).getOutput().getAmount()); + ItemStack item = ((CraftingRecipe) recipe.getRecipe()).getPreviewItemStack(); + item.setAmount(((CraftingRecipe) recipe.getRecipe()).getOutputAmount()); inv.setItem(16, item); } if (recipe.getRecipe() instanceof UpgradingRecipe) { diff --git a/src/main/java/net/Indyuce/mmoitems/gui/CraftingStationView.java b/src/main/java/net/Indyuce/mmoitems/gui/CraftingStationView.java index 799606af..9627547a 100644 --- a/src/main/java/net/Indyuce/mmoitems/gui/CraftingStationView.java +++ b/src/main/java/net/Indyuce/mmoitems/gui/CraftingStationView.java @@ -181,7 +181,7 @@ public class CraftingStationView extends PluginInventory { * to the player and remove the recipe from the queue */ if (recipeInfo.isReady()) { - ItemStack result = recipe.hasOption(Recipe.RecipeOption.OUTPUT_ITEM) ? recipe.getOutput().generate(playerData.getRPG()) : null; + ItemStack result = recipe.hasOption(Recipe.RecipeOption.OUTPUT_ITEM) ? recipe.getOutputItemStack(playerData.getRPG()) : null; PlayerUseCraftingStationEvent called = new PlayerUseCraftingStationEvent(playerData, station, recipe, result); Bukkit.getPluginManager().callEvent(called); diff --git a/src/main/java/net/Indyuce/mmoitems/gui/edition/AbilityEdition.java b/src/main/java/net/Indyuce/mmoitems/gui/edition/AbilityEdition.java index 0891ebbc..63d9f8c0 100644 --- a/src/main/java/net/Indyuce/mmoitems/gui/edition/AbilityEdition.java +++ b/src/main/java/net/Indyuce/mmoitems/gui/edition/AbilityEdition.java @@ -69,7 +69,7 @@ public class AbilityEdition extends EditionInventory { if (ability != null) { String castModeConfigString = getEditedSection().getString("ability." + configKey + ".mode"); String castModeFormat = castModeConfigString == null ? "" - : castModeConfigString.toUpperCase().replace(" ", "_").replace("-", "_").replaceAll("[^A-Z_]", ""); + : castModeConfigString.toUpperCase().replace(" ", "_").replace("-", "_").replaceAll("[^A-Z0-9_]", ""); TriggerType castMode = TriggerType.safeValueOf(castModeFormat); ItemStack castModeItem = new ItemStack(Material.ARMOR_STAND); diff --git a/src/main/java/net/Indyuce/mmoitems/listener/ItemUse.java b/src/main/java/net/Indyuce/mmoitems/listener/ItemUse.java index 469689f8..f1946305 100644 --- a/src/main/java/net/Indyuce/mmoitems/listener/ItemUse.java +++ b/src/main/java/net/Indyuce/mmoitems/listener/ItemUse.java @@ -12,6 +12,8 @@ import net.Indyuce.mmoitems.MMOUtils; import net.Indyuce.mmoitems.api.ItemAttackMetadata; import net.Indyuce.mmoitems.api.Type; import net.Indyuce.mmoitems.api.TypeSet; +import net.Indyuce.mmoitems.api.event.MMOItemsProjectileFireEvent; +import net.Indyuce.mmoitems.api.event.MMOItemsSpecialWeaponAttack; import net.Indyuce.mmoitems.api.interaction.*; import net.Indyuce.mmoitems.api.interaction.weapon.Gauntlet; import net.Indyuce.mmoitems.api.interaction.weapon.Weapon; @@ -20,6 +22,7 @@ import net.Indyuce.mmoitems.api.interaction.weapon.untargeted.UntargetedWeapon; import net.Indyuce.mmoitems.api.interaction.weapon.untargeted.UntargetedWeapon.WeaponType; import net.Indyuce.mmoitems.api.player.PlayerData; import net.Indyuce.mmoitems.api.util.message.Message; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -95,10 +98,21 @@ public class ItemUse implements Listener { } if (useItem instanceof UntargetedWeapon) { + UntargetedWeapon weapon = (UntargetedWeapon) useItem; if ((event.getAction().name().contains("RIGHT_CLICK") && weapon.getWeaponType() == WeaponType.RIGHT_CLICK) - || (event.getAction().name().contains("LEFT_CLICK") && weapon.getWeaponType() == WeaponType.LEFT_CLICK)) + || (event.getAction().name().contains("LEFT_CLICK") && weapon.getWeaponType() == WeaponType.LEFT_CLICK)) { + MMOItems.log("Running from \u00a7cUNTARGETTED"); + + // Run attack event + MMOItemsSpecialWeaponAttack attackEvent = new MMOItemsSpecialWeaponAttack(player, useItem, weapon.untargetedTargetTrace(EquipmentSlot.fromBukkit(event.getHand()))); + Bukkit.getPluginManager().callEvent(attackEvent); + + // Cancelled? + if (attackEvent.isCancelled()) { return; } + weapon.untargetedAttack(EquipmentSlot.fromBukkit(event.getHand())); + } } } @@ -186,12 +200,33 @@ public class ItemUse implements Listener { return; // Special staff attack - if (weapon instanceof Staff) + if (weapon instanceof Staff) { + MMOItems.log("Running from \u00a7aSTAFF"); + // Run attack event + MMOItemsSpecialWeaponAttack attackEvent = new MMOItemsSpecialWeaponAttack(player, weapon, target); + Bukkit.getPluginManager().callEvent(attackEvent); + + // Cancelled? + if (attackEvent.isCancelled()) { return; } + + // Run attack ((Staff) weapon).specialAttack(target); + } // Special gauntlet attack - if (weapon instanceof Gauntlet) + if (weapon instanceof Gauntlet) { + MMOItems.log("Running from \u00a7bGAUNTLET"); + + // Run attack event + MMOItemsSpecialWeaponAttack attackEvent = new MMOItemsSpecialWeaponAttack(player, weapon, target); + Bukkit.getPluginManager().callEvent(attackEvent); + + // Cancelled? + if (attackEvent.isCancelled()) { return; } + + // Run attack ((Gauntlet) weapon).specialAttack(target); + } } // TODO: Rewrite this with a custom 'ApplyMMOItemEvent'? @@ -289,8 +324,7 @@ public class ItemUse implements Listener { Arrow arrow = (Arrow) event.getProjectile(); if (item.getStat("ARROW_VELOCITY") > 0) arrow.setVelocity(arrow.getVelocity().multiply(item.getStat("ARROW_VELOCITY"))); - MMOItems.plugin.getEntities().registerCustomProjectile(item, playerData.getStats().newTemporary(bowSlot), event.getProjectile(), type != null, - event.getForce()); + MMOItems.plugin.getEntities().registerCustomProjectile(item, playerData.getStats().newTemporary(bowSlot), event.getProjectile(), event, type != null, event.getForce()); } /** diff --git a/src/main/java/net/Indyuce/mmoitems/manager/ConfigManager.java b/src/main/java/net/Indyuce/mmoitems/manager/ConfigManager.java index 5d5edfb0..37b94126 100644 --- a/src/main/java/net/Indyuce/mmoitems/manager/ConfigManager.java +++ b/src/main/java/net/Indyuce/mmoitems/manager/ConfigManager.java @@ -22,6 +22,7 @@ import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; @@ -255,8 +256,8 @@ public class ConfigManager implements Reloadable { return configName != null ? configName : ability.getName(); } - public String getCastingModeName(TriggerType mode) { - return abilities.getConfig().getString("cast-mode." + mode.getLowerCaseId()); + @NotNull public String getCastingModeName(@NotNull TriggerType mode) { + return abilities.getConfig().getString("cast-mode." + mode.getLowerCaseId(), mode.name()); } @Deprecated diff --git a/src/main/java/net/Indyuce/mmoitems/manager/EntityManager.java b/src/main/java/net/Indyuce/mmoitems/manager/EntityManager.java index 17892b5e..70ab27bc 100644 --- a/src/main/java/net/Indyuce/mmoitems/manager/EntityManager.java +++ b/src/main/java/net/Indyuce/mmoitems/manager/EntityManager.java @@ -6,6 +6,7 @@ import io.lumine.mythic.lib.damage.DamageType; import io.lumine.mythic.lib.player.PlayerMetadata; import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.api.ItemAttackMetadata; +import net.Indyuce.mmoitems.api.event.MMOItemsProjectileFireEvent; import net.Indyuce.mmoitems.api.interaction.projectile.ArrowParticles; import net.Indyuce.mmoitems.api.interaction.projectile.EntityData; import net.Indyuce.mmoitems.api.interaction.projectile.ProjectileData; @@ -20,6 +21,9 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.entity.EntityShootBowEvent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.HashMap; import java.util.Map; @@ -36,10 +40,8 @@ public class EntityManager implements Listener { private final WeakHashMap projectiles = new WeakHashMap<>(); - public void registerCustomProjectile(NBTItem sourceItem, PlayerMetadata attacker, Entity entity, boolean customWeapon) { - registerCustomProjectile(sourceItem, attacker, entity, customWeapon, 1); - } - + @Deprecated public void registerCustomProjectile(NBTItem sourceItem, PlayerMetadata attacker, Entity entity, boolean customWeapon) { registerCustomProjectile(sourceItem, attacker, entity, customWeapon, 1); } + @Deprecated public void registerCustomProjectile(@NotNull NBTItem sourceItem, @NotNull PlayerMetadata attacker, @NotNull Entity entity, boolean customWeapon, double damageMultiplicator) { registerCustomProjectile(sourceItem, attacker, entity, null, customWeapon, damageMultiplicator); } /** * Registers a custom projectile. This is used for bows, crossbows and tridents. *

@@ -47,12 +49,13 @@ public class EntityManager implements Listener { * * @param sourceItem Item used to shoot the projectile * @param attacker Cached stats of the player shooting the projectile + * @param shootEvent Event that caused this projectile registration. * @param entity The custom entity * @param customWeapon Is the source weapon is a custom item * @param damageMultiplicator The damage coefficient. For bows, this is basically the pull force. * For tridents or anything else this is always set to 1 */ - public void registerCustomProjectile(NBTItem sourceItem, PlayerMetadata attacker, Entity entity, boolean customWeapon, double damageMultiplicator) { + public void registerCustomProjectile(@NotNull NBTItem sourceItem, @NotNull PlayerMetadata attacker, @NotNull Entity entity, @Nullable EntityShootBowEvent shootEvent, boolean customWeapon, double damageMultiplicator) { /* * For bows, MC default value is 7. When using custom bows, the attack @@ -64,10 +67,16 @@ public class EntityManager implements Listener { * and 1 for bows, and it's always 1 for tridents or crossbows. */ double damage = attacker.getStat("ATTACK_DAMAGE"); - damage = (customWeapon ? damage : 5 + damage) * damageMultiplicator; - ItemAttackMetadata attackMeta = new ItemAttackMetadata(new DamageMetadata(damage, DamageType.WEAPON, DamageType.PHYSICAL, DamageType.PROJECTILE), attacker); - attacker.setStat("ATTACK_DAMAGE", damage); + // Sweet event + MMOItemsProjectileFireEvent event = new MMOItemsProjectileFireEvent(attacker, entity, sourceItem, shootEvent, (customWeapon ? damage : 5 + damage), damageMultiplicator); + Bukkit.getPluginManager().callEvent(event); + + // Update based one vent + double finalDamage = event.getFinalDamage(); + + ItemAttackMetadata attackMeta = new ItemAttackMetadata(new DamageMetadata(finalDamage, event.getDamageTypes()), attacker); + attacker.setStat("ATTACK_DAMAGE", finalDamage); /* * Load arrow particles if the entity is an arrow and if the item has diff --git a/src/main/java/net/Indyuce/mmoitems/stat/Abilities.java b/src/main/java/net/Indyuce/mmoitems/stat/Abilities.java index 61138261..7357e8f7 100644 --- a/src/main/java/net/Indyuce/mmoitems/stat/Abilities.java +++ b/src/main/java/net/Indyuce/mmoitems/stat/Abilities.java @@ -113,7 +113,8 @@ public class Abilities extends ItemStat { String configKey = (String) info[0]; String edited = (String) info[1]; - String format = message.toUpperCase().replace("-", "_").replace(" ", "_").replaceAll("[^A-Z_]", ""); + String format = message.toUpperCase().replace("-", "_").replace(" ", "_").replaceAll("[^A-Z0-9_]", ""); + if (edited.equals("ability")) { Validate.isTrue(MMOItems.plugin.getSkills().hasSkill(format), "format is not a valid ability! You may check the ability list using /mi list ability."); @@ -128,7 +129,9 @@ public class Abilities extends ItemStat { } if (edited.equals("mode")) { + TriggerType castMode = TriggerType.valueOf(format); + inv.getEditedSection().set("ability." + configKey + ".mode", castMode.name()); inv.registerTemplateEdition(); inv.getPlayer().sendMessage(MMOItems.plugin.getPrefix() + "Successfully set the trigger to " + ChatColor.GOLD + castMode.getName() diff --git a/src/main/java/net/Indyuce/mmoitems/stat/data/AbilityData.java b/src/main/java/net/Indyuce/mmoitems/stat/data/AbilityData.java index 0230bf46..448a7b43 100644 --- a/src/main/java/net/Indyuce/mmoitems/stat/data/AbilityData.java +++ b/src/main/java/net/Indyuce/mmoitems/stat/data/AbilityData.java @@ -28,8 +28,8 @@ import java.util.Set; public class AbilityData extends Skill { private final RegisteredSkill ability; - private final TriggerType triggerType; - private final Map modifiers = new HashMap<>(); + @NotNull private final TriggerType triggerType; + @NotNull private final Map modifiers = new HashMap<>(); public AbilityData(JsonObject object) { ability = MMOItems.plugin.getSkills().getSkill(object.get("Id").getAsString()); @@ -63,9 +63,7 @@ public class AbilityData extends Skill { return ability; } - public TriggerType getTriggerType() { - return triggerType; - } + @NotNull public TriggerType getTriggerType() { return triggerType; } public Set getModifiers() { return modifiers.keySet(); @@ -80,7 +78,6 @@ public class AbilityData extends Skill { return modifiers.containsKey(path); } - @NotNull @Override public boolean getResult(SkillMetadata meta) {