* These stats are only updated on a server reload because that
- * class has to be instanciated again for the registered stats to update
+ * class has to be instantiated again for the registered stats to update
*/
public MMOCoreHook() {
for (PlayerAttribute attribute : MMOCore.plugin.attributeManager.getAll())
MMOItems.plugin.getStats().register(new Required_Attribute(attribute));
- for (Profession profession : MMOCore.plugin.professionManager.getAll())
+ for (Profession profession : MMOCore.plugin.professionManager.getAll()) {
+
+ // Adds profession specific Additional Experience stats.
+ MMOItems.plugin.getStats().register(new DoubleStat((StatType.ADDITIONAL_EXPERIENCE.name() + '_' + profession.getId())
+ .replace('-', '_').replace(' ', '_').toUpperCase(Locale.ROOT),
+ VersionMaterial.EXPERIENCE_BOTTLE.toMaterial(), profession.getName() + ' ' + "Additional Experience (MMOCore)"
+ , new String[]{"Additional MMOCore profession " + profession.getName() + " experience in %."}, new String[]{"!block", "all"}));
+
MMOItems.plugin.getStats().register(new Required_Profession(profession));
+ }
}
@Override
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
@@ -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