Merge remote-tracking branch 'origin/master'

This commit is contained in:
Jules 2022-02-06 21:38:50 +01:00
commit 0b27f8ca55
33 changed files with 1055 additions and 94 deletions

View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>net.Indyuce</groupId>
<artifactId>MMOItems</artifactId>
<version>6.7-SNAPSHOT</version>
<version>6.7.1-SNAPSHOT</version>
<name>MMOItems</name>
<description>A great item solution for your RPG server!!</description>
@ -141,7 +141,7 @@
<dependency>
<groupId>io.lumine</groupId>
<artifactId>MythicLib-dist</artifactId>
<version>1.3-R22-SNAPSHOT</version>
<version>1.3-R25-SNAPSHOT</version>
<scope>provided</scope>
</dependency>

View File

@ -96,7 +96,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; }
@ -800,6 +800,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

View File

@ -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;
}
}

View File

@ -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; }
}

View File

@ -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);
}
}

View File

@ -16,7 +16,7 @@ import org.jetbrains.annotations.NotNull;
*/
public abstract class Ingredient<C extends PlayerIngredient> {
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<C extends PlayerIngredient> {
return id;
}
public void setAmount(int amount) { this.amount = amount; }
public int getAmount() {
return amount;
}

View File

@ -22,8 +22,7 @@ import org.jetbrains.annotations.NotNull;
public class MMOItemIngredient extends Ingredient<MMOItemPlayerIngredient> {
private final MMOItemTemplate template;
@NotNull
private final QuickNumberRange level;
@NotNull private final QuickNumberRange level;
private final String display;
public MMOItemIngredient(MMOLineConfig config) {

View File

@ -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<VanillaPlayerIngredient> {
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);
if (vanillaBackward) {
// Check for material
if (ing.getType() != material)
return false;
if (ing.getType() != material) {
//VING//MMOItems.log("\u00a78VING\u00a79 MCH\u00a7c Not right material \u00a78(expected \u00a76" + material.toString() + "\u00a78)");
return false; }
//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); }
}

View File

@ -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() {

View File

@ -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);
}
public double getCraftingTime() {
return craftingTime;
// 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() {
// Generate as MMOItem
if (identifiedMMO != 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.getPreview();
}
public boolean isInstant() {
return craftingTime <= 0;
}
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) {

View File

@ -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.
* <br><br>
* Contains information even on the temporary stats the player had when
* they executed the action, if that matters for some reason...
* <br><br>
* 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
}

View File

@ -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
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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);

View File

@ -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());

View File

@ -1,34 +1,47 @@
package net.Indyuce.mmoitems.comp.mmocore;
import io.lumine.mythic.lib.version.VersionMaterial;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.event.PlayerChangeClassEvent;
import net.Indyuce.mmocore.api.event.PlayerLevelUpEvent;
import net.Indyuce.mmocore.api.event.PlayerResourceUpdateEvent;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.player.attribute.PlayerAttribute;
import net.Indyuce.mmocore.api.player.stats.StatType;
import net.Indyuce.mmocore.experience.Profession;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.player.RPGPlayer;
import net.Indyuce.mmoitems.comp.mmocore.stat.Required_Attribute;
import net.Indyuce.mmoitems.comp.mmocore.stat.Required_Profession;
import net.Indyuce.mmoitems.comp.rpg.RPGHandler;
import net.Indyuce.mmoitems.stat.type.DoubleStat;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import java.util.Locale;
public class MMOCoreHook implements RPGHandler, Listener {
/**
* Called when MMOItems enables
* <p>
* 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
public void refreshStats(net.Indyuce.mmoitems.api.player.PlayerData data) {

View File

@ -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

View File

@ -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<Arrow> 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);
}
}

View File

@ -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<UseItemTypes> 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<String> 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<Skill> 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<Skill> 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;
}
}
}

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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,12 +98,23 @@ 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()));
}
}
}
@EventHandler(ignoreCancelled = true)
public void meleeAttacks(PlayerAttackEvent event) {
@ -186,13 +200,34 @@ 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'?
@EventHandler
@ -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());
}
/**

View File

@ -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

View File

@ -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<Integer, ProjectileData> 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.
* <p>
@ -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

View File

@ -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()

View File

@ -28,8 +28,8 @@ import java.util.Set;
public class AbilityData extends Skill {
private final RegisteredSkill ability;
private final TriggerType triggerType;
private final Map<String, Double> modifiers = new HashMap<>();
@NotNull private final TriggerType triggerType;
@NotNull private final Map<String, Double> 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<String> getModifiers() {
return modifiers.keySet();
@ -80,7 +78,6 @@ public class AbilityData extends Skill {
return modifiers.containsKey(path);
}
@NotNull
@Override
public boolean getResult(SkillMetadata meta) {

View File

@ -98,6 +98,14 @@ lore-format:
- '#repair-percent#'
- '#item-cooldown#'
- '#additional-experience#'
- '#additional-experience-alchemy#'
- '#additional-experience-enchanting#'
- '#additional-experience-farming#'
- '#additional-experience-fishing#'
- '#additional-experience-mining#'
- '#additional-experience-smelting#'
- '#additional-experience-smithing#'
- '#additional-experience-woodcutting#'
- '#cooldown-reduction#'
- '#mana-cost#'
- '#stamina-cost#'

View File

@ -64,6 +64,14 @@ max-stamina: '&3 &7■ Max Stamina: &f<plus>#'
stamina-regeneration: '&3 &7■ Stamina Regeneration: &f<plus>#'
cooldown-reduction: '&3 &7■ Skill Cooldown Reduction: &f<plus>#%'
additional-experience: '&3 &7■ Additional Experience: &f<plus>#%'
additional-experience-alchemy: '&7■ Additional Alchemy Experience: &f<plus>#%'
additional-experience-enchanting: '&7■ Additional Enchanting Experience: &f<plus>#%'
additional-experience-farming: '&7■ Additional Farming Experience: &f<plus>#%'
additional-experience-fishing: '&7■ Additional Fishing Experience: &f<plus>#%'
additional-experience-mining: '&7■ Additional Mining Experience: &f<plus>#%'
additional-experience-smelting: '&7■ Additional Smelting Experience: &f<plus>#%'
additional-experience-smithing: '&7■ Additional Smithing Experience: &f<plus>#%'
additional-experience-woodcutting: '&7■ Additional Woodcutting Experience: &f<plus>#%'
# Extra Options
perm-effect: '&3 &7■ Permanent &f#'