More cleanup

This commit is contained in:
Indyuce 2020-08-09 14:38:08 +02:00
parent 8e5610e143
commit c1210e06ba
5 changed files with 156 additions and 80 deletions

View File

@ -1,6 +1,7 @@
package net.Indyuce.mmoitems.api;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang.Validate;
import org.bukkit.Material;
@ -52,7 +53,7 @@ public class Type {
private final String id;
private String name;
private TypeSet set;
private final TypeSet set;
/*
* the 'weapon' boolean is used for item type restrictions for gem stones to
@ -76,7 +77,7 @@ public class Type {
* any type can have a subtype which basically dictates what the item type
* does.
*/
private Type parent = null;
private Type parent;
private UnidentifiedItem unidentifiedTemplate;
@ -84,7 +85,7 @@ public class Type {
* list of stats which can be applied onto an item which has this type. This
* improves performance when generating an item by a significant amount.
*/
private List<ItemStat> available;
private final Set<ItemStat> available = new HashSet<>();
public Type(TypeSet set, String id, boolean weapon, boolean melee, boolean rightClickSpecial, EquipmentSlot equipType) {
this.set = set;
@ -172,10 +173,19 @@ public class Type {
return getItemSet() == set;
}
public List<ItemStat> getAvailableStats() {
/**
* @return The collection of all stats which can be applied onto this
* specific item type. This list is cached when types are being
* loaded and is a PRETTY GOOD performance improvement.
*/
public Set<ItemStat> getAvailableStats() {
return available;
}
/**
* @return Finds the /item config file corresponding to the item type and
* loads it
*/
public ConfigFile getConfigFile() {
return new ConfigFile("/item", getId().toLowerCase());
}
@ -184,21 +194,14 @@ public class Type {
return unidentifiedTemplate;
}
public void cacheAvailableStats(List<ItemStat> stats) {
available = stats;
}
/**
* @param stat
* The stat to check
* @return If the stat can be handled by this type of item
*/
@Deprecated
public boolean canHave(ItemStat stat) {
if (isSubtype())
return getParent().canHave(stat);
for (String s1 : stat.getCompatibleTypes()) {
if (s1.equalsIgnoreCase("!" + getId()))
return false;
if (s1.equalsIgnoreCase(getId()) || s1.equalsIgnoreCase(set.name()) || s1.equalsIgnoreCase("all"))
return true;
}
return false;
return stat.isCompatible(this);
}
@Override
@ -214,19 +217,39 @@ public class Type {
return split.length > 1 ? MMOLib.plugin.getVersion().getWrapper().textureItem(material, Integer.parseInt(split[1])) : new ItemStack(material);
}
/**
*
* @param item
* The item to retrieve the type from
* @return The type of the item.
* @deprecated Really heavy method because it instantiates an NBTItem (reads
* through all the item NBT data), looks for the type tag and
* does a type map lookup. Use NBTItem#get(ItemStack) first and
* then NBTItem#getType()
*/
@Deprecated
public static Type get(ItemStack item) {
return MMOLib.plugin.getNMS().getNBTItem(item).getType();
}
/*
* methods used in command executors and completions for a faster access to
* the typeManager instance, therefore no need to replace _ for " "
/**
* Used in command executors and completions for easier manipulation
*
* @param id
* The type id
* @return The type or NPE if it couldn't be found
*/
public static Type get(String id) {
return MMOItems.plugin.getTypes().get(id.toUpperCase().replace("-", "_").replace(" ", "_"));
}
/**
* Used in command executors and completions for easier manipulation
*
* @param id
* The type id
* @return If a registered type with this ID could be found
*/
public static boolean isValid(String id) {
return MMOItems.plugin.getTypes().has(id.toUpperCase().replace("-", "_").replace(" ", "_"));
}

View File

@ -20,8 +20,8 @@ import net.mmogroup.mmolib.version.VersionSound;
public enum TypeSet {
/*
* slashing weapons deal damage in a cone behind the player's initial
/**
* Slashing weapons deal damage in a cone behind the player's initial
* target, which makes it a deadly AoE weapon for warriors
*/
SLASHING((stats, target, weapon, result) -> {
@ -37,12 +37,15 @@ public enum TypeSet {
loc.getWorld().spawnParticle(Particle.CRIT, loc.clone().add(Math.cos(a + a1) * r, Math.sin(p) * r, Math.sin(a + a1) * r), 0);
for (Entity entity : MMOUtils.getNearbyChunkEntities(loc))
if (entity.getLocation().distanceSquared(loc) < 40 && stats.getPlayer().getEyeLocation().getDirection().angle(entity.getLocation().subtract(stats.getPlayer().getLocation()).toVector()) < Math.PI / 3 && MMOUtils.canDamage(stats.getPlayer(), entity) && !entity.equals(target))
if (entity.getLocation().distanceSquared(loc) < 40
&& stats.getPlayer().getEyeLocation().getDirection()
.angle(entity.getLocation().subtract(stats.getPlayer().getLocation()).toVector()) < Math.PI / 3
&& MMOUtils.canDamage(stats.getPlayer(), entity) && !entity.equals(target))
result.clone().multiplyDamage(.4).applyEffectsAndDamage(stats, weapon.getNBTItem(), (LivingEntity) entity);
}),
/*
* piercing weapons deal damage in a line behind the initial target, which
/**
* Piercing weapons deal damage in a line behind the initial target, which
* is harder to land than a slashing weapon but the AoE damage ratio is
* increased which makes it a perfect 'double or nothing' weapon for
* assassins
@ -60,12 +63,15 @@ public enum TypeSet {
loc.getWorld().spawnParticle(Particle.CRIT, loc.clone().add(Math.cos(a + a1) * r, Math.sin(p) * r, Math.sin(a + a1) * r), 0);
for (Entity entity : MMOUtils.getNearbyChunkEntities(loc))
if (entity.getLocation().distanceSquared(stats.getPlayer().getLocation()) < 40 && stats.getPlayer().getEyeLocation().getDirection().angle(entity.getLocation().toVector().subtract(stats.getPlayer().getLocation().toVector())) < Math.PI / 18 && MMOUtils.canDamage(stats.getPlayer(), entity) && !entity.equals(target))
if (entity.getLocation().distanceSquared(stats.getPlayer().getLocation()) < 40
&& stats.getPlayer().getEyeLocation().getDirection()
.angle(entity.getLocation().toVector().subtract(stats.getPlayer().getLocation().toVector())) < Math.PI / 18
&& MMOUtils.canDamage(stats.getPlayer(), entity) && !entity.equals(target))
result.clone().multiplyDamage(.4).applyEffectsAndDamage(stats, weapon.getNBTItem(), (LivingEntity) entity);
}),
/*
* blunt weapons are like 1.9 sweep attacks. they damage all enemies nearby
/**
* Blunt weapons are like 1.9 sweep attacks. They damage all enemies nearby
* and apply a slight knockback
*/
BLUNT((stats, target, weapon, result) -> {
@ -77,50 +83,52 @@ public enum TypeSet {
target.getWorld().spawnParticle(Particle.EXPLOSION_LARGE, target.getLocation().add(0, 1, 0), 0);
double bluntPower = stats.getStat(ItemStat.BLUNT_POWER);
if (bluntPower > 0) {
double bluntRating = weapon.getValue(stats.getStat(ItemStat.BLUNT_RATING), MMOItems.plugin.getConfig().getDouble("default.blunt-rating")) / 100;
double bluntRating = weapon.getValue(stats.getStat(ItemStat.BLUNT_RATING),
MMOItems.plugin.getConfig().getDouble("default.blunt-rating")) / 100;
for (Entity entity : target.getNearbyEntities(bluntPower, bluntPower, bluntPower))
if (MMOUtils.canDamage(stats.getPlayer(), entity) && !entity.equals(target))
result.clone().multiplyDamage(bluntRating).applyEffectsAndDamage(stats, weapon.getNBTItem(), (LivingEntity) entity);
}
}
if (MMOItems.plugin.getConfig().getBoolean("item-ability.blunt.stun.enabled") && !stats.getData().isOnCooldown(CooldownType.SPECIAL_ATTACK) && random.nextDouble() < MMOItems.plugin.getConfig().getDouble("item-ability.blunt.stun.chance") / 100) {
if (MMOItems.plugin.getConfig().getBoolean("item-ability.blunt.stun.enabled") && !stats.getData().isOnCooldown(CooldownType.SPECIAL_ATTACK)
&& random.nextDouble() < MMOItems.plugin.getConfig().getDouble("item-ability.blunt.stun.chance") / 100) {
stats.getData().applyCooldown(CooldownType.SPECIAL_ATTACK, MMOItems.plugin.getConfig().getDouble("item-ability.blunt.stun.cooldown"));
target.getWorld().playSound(target.getLocation(), VersionSound.ENTITY_ZOMBIE_ATTACK_WOODEN_DOOR.toSound(), 1, 2);
target.removePotionEffect(PotionEffectType.SLOW);
target.removePotionEffect(PotionEffectType.BLINDNESS);
target.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 20, 0));
target.addPotionEffect(new PotionEffect(PotionEffectType.SLOW, (int) (30 * MMOItems.plugin.getConfig().getDouble("item-ability.blunt.stun.power")), 1));
target.addPotionEffect(
new PotionEffect(PotionEffectType.SLOW, (int) (30 * MMOItems.plugin.getConfig().getDouble("item-ability.blunt.stun.power")), 1));
Location loc = target.getLocation();
loc.setYaw((float) (loc.getYaw() + 2 * (random.nextDouble() - .5) * 90));
loc.setPitch((float) (loc.getPitch() + 2 * (random.nextDouble() - .5) * 30));
}
}),
/*
* any item type can may apply their stats even when worn in offhand.
* they're the only items with that specific property
/**
* Any item type can may apply their stats even when worn in offhand.
* They're the only items with that specific property
*/
OFFHAND,
/*
* ranged attacks based weapons. when the player is too squishy to fight in
/**
* Ranged attacks based weapons. when the player is too squishy to fight in
* the middle of the battle-field, these weapons allow him to take some
* distance and still deal some good damage
*/
RANGE,
/*
* any other item type, like armor, consumables, etc. they all have their
/**
* Any other item type, like armor, consumables, etc. They all have their
* very specific passive depending on their item type
*/
EXTRA;
private SetAttackHandler<CachedStats, LivingEntity, Weapon, ItemAttackResult> attackHandler;
private final SetAttackHandler<CachedStats, LivingEntity, Weapon, ItemAttackResult> attackHandler;
private TypeSet() {
this((playerStats, target, weapon, result) -> {
});
this(null);
}
private TypeSet(SetAttackHandler<CachedStats, LivingEntity, Weapon, ItemAttackResult> attackHandler) {

View File

@ -46,17 +46,28 @@ public class StatManager {
return stats.values();
}
/*
* cache specific stats for better performance using these extra sets
/**
* @return Collection of all stats which are based on vanilla player
* attributes like movement speed, attack damage, max health..
*/
public Set<AttributeStat> getAttributeStats() {
return attributeBased;
}
/**
* @return Collection of all numeric stats like atk damage, crit strike
* chance, max mana... which can be applied on a gem stone. This is
* used when applying gem stones to quickly access all the stats
* which needs to be applied
*/
public Set<DoubleStat> getNumericStats() {
return numeric;
}
/**
* @return Collection of all stats which constitute an item restriction:
* required level, required class, soulbound..
*/
public Set<ItemRestriction> getItemRestrictionStats() {
return itemRestriction;
}
@ -65,16 +76,32 @@ public class StatManager {
return stats.containsKey(id);
}
public ItemStat get(String id) {
return stats.getOrDefault(id, null);
}
/**
* Registers a stat in MMOItems
*
* @param id
* Useless.
* @param stat
* The stat instance
* @deprecated Stat IDs are now stored in the stat instance directly. Please
* use StatManager#register(ItemStat) instead
*/
@Deprecated
public void register(String id, ItemStat stat) {
MMOItems.plugin.getLogger().log(Level.WARNING,
"Stat IDs are now stored in the stat instance directly. You must now use StatManager#register(ItemStat)");
register(stat);
}
/*
* the extra checks in that method to register stats even after the plugin
* has successfully enabled otherwise the other sets would not be updated.
/**
* Registers a stat in MMOItems. It must be done right after MMOItems loads
* before any manager is initialized because stats are commonly used when
* loading configs.
*
* @param stat
* The stat to register
*/
public void register(ItemStat stat) {
if (!stat.isEnabled())
@ -82,7 +109,7 @@ public class StatManager {
stats.put(stat.getId(), stat);
if (stat instanceof DoubleStat && !(stat instanceof ProperStat) && Type.GEM_STONE.canHave(stat))
if (stat instanceof DoubleStat && !(stat instanceof ProperStat) && stat.isCompatible(Type.GEM_STONE))
numeric.add((DoubleStat) stat);
if (stat instanceof AttributeStat)
@ -93,15 +120,14 @@ public class StatManager {
/*
* cache stat for every type which may have this stat. really important
* otherwise the stat will NOT be used anywhere in the plugin.
* otherwise the stat will NOT be used anywhere in the plugin. this
* process is also done in the TypeManager when registering new types
* but since stats can be registered after types are loaded, we must
* take it into account
*/
if (MMOItems.plugin.getTypes() != null)
for (Type type : MMOItems.plugin.getTypes().getAll())
if (type.canHave(stat))
if (stat.isCompatible(type))
type.getAvailableStats().add(stat);
}
public ItemStat get(String str) {
return stats.containsKey(str) ? stats.get(str) : null;
}
}

View File

@ -5,7 +5,6 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.lang.Validate;
@ -17,9 +16,13 @@ import net.Indyuce.mmoitems.manager.ConfigManager.DefaultFile;
public class TypeManager {
private final Map<String, Type> map = new LinkedHashMap<>();
/**
* Reloads the type manager. It entirely empties the currently registered
* item types, registers default item types again and reads item-types.yml
*/
public void reload() {
map.clear();
addAll(Type.ACCESSORY, Type.ARMOR, Type.BOW, Type.CATALYST, Type.CONSUMABLE, Type.CROSSBOW, Type.DAGGER, Type.GAUNTLET, Type.GEM_STONE,
registerAll(Type.ACCESSORY, Type.ARMOR, Type.BOW, Type.CATALYST, Type.CONSUMABLE, Type.CROSSBOW, Type.DAGGER, Type.GAUNTLET, Type.GEM_STONE,
Type.SKIN, Type.HAMMER, Type.LUTE, Type.MISCELLANEOUS, Type.MUSKET, Type.OFF_CATALYST, Type.ORNAMENT, Type.SPEAR, Type.STAFF,
Type.SWORD, Type.TOOL, Type.WHIP);
@ -34,51 +37,42 @@ public class TypeManager {
for (String id : config.getConfig().getKeys(false))
if (!map.containsKey(id))
try {
add(new Type(this, config.getConfig().getConfigurationSection(id)));
register(new Type(this, config.getConfig().getConfigurationSection(id)));
} catch (IllegalArgumentException exception) {
MMOItems.plugin.getLogger().log(Level.WARNING, "Could not register the type " + id + ": " + exception.getMessage());
MMOItems.plugin.getLogger().log(Level.WARNING, "Could not register type '" + id + "': " + exception.getMessage());
}
/*
* reload names & display items from types and generate corresponding
* config files.
*/
for (Iterator<Type> iterator = map.values().iterator(); iterator.hasNext();) {
Type type = iterator.next();
try {
type.load(config.getConfig().getConfigurationSection(type.getId()));
} catch (IllegalArgumentException exception) {
MMOItems.plugin.getLogger().log(Level.WARNING, "Could not register the type " + type.getId() + ": " + exception.getMessage());
MMOItems.plugin.getLogger().log(Level.WARNING, "Could not register type '" + type.getId() + "': " + exception.getMessage());
iterator.remove();
continue;
}
String path = type.getId().toLowerCase().replace("_", "-");
if (!config.getConfig().contains(path))
config.getConfig().set(path, type.getName());
/*
* caches all the stats which the type can have to reduce future
* both item generation (and GUI) calculations. probably the thing
* which takes the most time when loading item types.
*/
type.cacheAvailableStats(MMOItems.plugin.getStats().getAll().stream().filter(stat -> type.canHave(stat)).collect(Collectors.toList()));
type.getAvailableStats().clear();
MMOItems.plugin.getStats().getAll().stream().filter(stat -> stat.isCompatible(type)).forEach(stat -> type.getAvailableStats().add(stat));
}
}
public void add(Type type) {
public void register(Type type) {
map.put(type.getId(), type);
}
public void addAll(Type... types) {
private void registerAll(Type... types) {
for (Type type : types)
add(type);
register(type);
}
/*
* TODO minor refactor, use Optional<Type>
*/
// TODO minor refactor, use Optional<Type>
public Type get(String id) {
return map.get(id);
}

View File

@ -1,5 +1,6 @@
package net.Indyuce.mmoitems.stat.type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@ -9,6 +10,7 @@ import org.bukkit.inventory.ItemStack;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.ConfigFile;
import net.Indyuce.mmoitems.api.Type;
import net.Indyuce.mmoitems.api.item.MMOItem;
import net.Indyuce.mmoitems.api.item.ReadMMOItem;
import net.Indyuce.mmoitems.api.item.build.MMOItemBuilder;
@ -263,7 +265,8 @@ public abstract class ItemStat {
private final String id, name;
private final ItemStack item;
private final String[] lore, compatibleTypes;
private final String[] lore;
private final List<String> compatibleTypes;
private final List<Material> compatibleMaterials;
/*
@ -295,7 +298,7 @@ public abstract class ItemStat {
this.id = id;
this.item = item;
this.lore = lore == null ? new String[0] : lore;
this.compatibleTypes = types == null ? new String[0] : types;
this.compatibleTypes = types == null ? new ArrayList<>() : Arrays.asList(types);
this.name = name;
this.compatibleMaterials = Arrays.asList(materials);
}
@ -384,6 +387,11 @@ public abstract class ItemStat {
return id;
}
/**
* @return The stat ID
* @deprecated Use getId() instead. Type is no longer an util since they can
* now be registered from external plugins
*/
@Deprecated
public String name() {
return id;
@ -393,6 +401,11 @@ public abstract class ItemStat {
return id.toLowerCase().replace("_", "-");
}
/**
* @return The NBT path used by the stat to save data in an item's NBTTags.
* The format is 'MMOITEMS_' followed by the stat name in capital
* letters only using _
*/
public String getNBTPath() {
return "MMOITEMS_" + id;
}
@ -409,10 +422,22 @@ public abstract class ItemStat {
return lore;
}
public String[] getCompatibleTypes() {
public List<String> getCompatibleTypes() {
return compatibleTypes;
}
/**
* @param type
* The item type to check
* @return If a certain item type is compatible with this item stat
*/
public boolean isCompatible(Type type) {
String lower = getId().toLowerCase();
return type.isSubtype() ? isCompatible(type.getParent())
: !compatibleTypes.contains("!" + lower) && (compatibleTypes.contains("all") || compatibleTypes.contains(lower)
|| compatibleTypes.contains(type.getItemSet().getName().toLowerCase()));
}
public boolean hasValidMaterial(ItemStack item) {
return compatibleMaterials.size() == 0 || compatibleMaterials.contains(item.getType());
}