Merge remote-tracking branch 'origin/master'

This commit is contained in:
Ka0rX 2023-07-09 16:10:34 +01:00
commit e0bdfa2000
25 changed files with 213 additions and 171 deletions

View File

@ -46,8 +46,8 @@ import net.Indyuce.mmocore.skill.binding.BoundSkillInfo;
import net.Indyuce.mmocore.skill.binding.SkillSlot; import net.Indyuce.mmocore.skill.binding.SkillSlot;
import net.Indyuce.mmocore.skill.cast.SkillCastingInstance; import net.Indyuce.mmocore.skill.cast.SkillCastingInstance;
import net.Indyuce.mmocore.skill.cast.SkillCastingMode; import net.Indyuce.mmocore.skill.cast.SkillCastingMode;
import net.Indyuce.mmocore.skilltree.SkillTreeStatus;
import net.Indyuce.mmocore.skilltree.SkillTreeNode; import net.Indyuce.mmocore.skilltree.SkillTreeNode;
import net.Indyuce.mmocore.skilltree.SkillTreeStatus;
import net.Indyuce.mmocore.skilltree.tree.SkillTree; import net.Indyuce.mmocore.skilltree.tree.SkillTree;
import net.Indyuce.mmocore.waypoint.Waypoint; import net.Indyuce.mmocore.waypoint.Waypoint;
import net.Indyuce.mmocore.waypoint.WaypointOption; import net.Indyuce.mmocore.waypoint.WaypointOption;
@ -745,6 +745,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
final int y = getPlayer().getLocation().getBlockY(); final int y = getPlayer().getLocation().getBlockY();
final int z = getPlayer().getLocation().getBlockZ(); final int z = getPlayer().getLocation().getBlockZ();
final int warpTime = target.getWarpTime(); final int warpTime = target.getWarpTime();
final boolean hasPerm = getPlayer().hasPermission("mmocore.bypass-waypoint-wait");
int t; int t;
public void run() { public void run() {
@ -757,7 +758,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
} }
MMOCore.plugin.configManager.getSimpleMessage("warping-comencing", "left", String.valueOf((warpTime - t) / 20)).send(getPlayer()); MMOCore.plugin.configManager.getSimpleMessage("warping-comencing", "left", String.valueOf((warpTime - t) / 20)).send(getPlayer());
if (t++ >= warpTime) { if (hasPerm || t++ >= warpTime) {
getPlayer().teleport(target.getLocation()); getPlayer().teleport(target.getLocation());
getPlayer().addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 20, 1, false, false)); getPlayer().addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 20, 1, false, false));
MMOCore.plugin.soundManager.getSound(SoundEvent.WARP_TELEPORT).playTo(getPlayer()); MMOCore.plugin.soundManager.getSound(SoundEvent.WARP_TELEPORT).playTo(getPlayer());
@ -1003,29 +1004,30 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
return skillCasting != null; return skillCasting != null;
} }
/** @Deprecated
* @return true if the PlayerEnterCastingModeEvent successfully put the player into casting mode, otherwise if the event is cancelled, returns false.
* @apiNote Changed to a boolean to reflect the cancellation state of the event being fired
*/
public boolean setSkillCasting(@NotNull SkillCastingInstance skillCasting) { public boolean setSkillCasting(@NotNull SkillCastingInstance skillCasting) {
Validate.isTrue(!isCasting(), "Player already in casting mode"); Validate.isTrue(!isCasting(), "Player already in casting mode");
PlayerEnterCastingModeEvent event = new PlayerEnterCastingModeEvent(getPlayer()); PlayerEnterCastingModeEvent event = new PlayerEnterCastingModeEvent(getPlayer());
Bukkit.getPluginManager().callEvent(event); Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return false;
if (event.isCancelled()){
skillCasting.close(); skillCasting.close();
return false; setSkillCasting();
}
this.skillCasting = skillCasting;
return true; return true;
} }
/** /**
* API Method * @return true if the PlayerEnterCastingModeEvent successfully put the player into casting mode, otherwise if the event is cancelled, returns false.
* @apiNote Changed to a boolean to reflect the cancellation state of the event being fired
*/ */
public void setSkillCasting() { public boolean setSkillCasting() {
Validate.isTrue(!isCasting(), "Player already in casting mode"); Validate.isTrue(!isCasting(), "Player already in casting mode");
setSkillCasting(SkillCastingMode.getCurrent().newInstance(this)); PlayerEnterCastingModeEvent event = new PlayerEnterCastingModeEvent(getPlayer());
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return false;
this.skillCasting = SkillCastingMode.getCurrent().newInstance(this);
return true;
} }
@NotNull @NotNull
@ -1035,27 +1037,25 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
/** /**
* API Method to leave casting mode and fire the PlayerExitCastingModeEvent * API Method to leave casting mode and fire the PlayerExitCastingModeEvent
*
* @return true if the skill casting mode was left, or false if the event was cancelled, keeping the player in casting mode. * @return true if the skill casting mode was left, or false if the event was cancelled, keeping the player in casting mode.
*/ */
public boolean leaveSkillCasting(){ public boolean leaveSkillCasting() {
return this.leaveSkillCasting(false); return leaveSkillCasting(false);
} }
/** /**
* @param skipEvent Skip Firing the PlayerExitCastingModeEvent * @param skipEvent Skip Firing the PlayerExitCastingModeEvent
* @return true if the PlayerExitCastingModeEvent is not cancelled, or if the event is skipped. * @return true if the PlayerExitCastingModeEvent is not cancelled, or if the event is skipped.
*
*/ */
public boolean leaveSkillCasting(boolean skipEvent) { public boolean leaveSkillCasting(boolean skipEvent) {
Validate.isTrue(isCasting(), "Player not in casting mode"); Validate.isTrue(isCasting(), "Player not in casting mode");
if (!skipEvent) { if (!skipEvent) {
PlayerExitCastingModeEvent event = new PlayerExitCastingModeEvent(getPlayer()); PlayerExitCastingModeEvent event = new PlayerExitCastingModeEvent(getPlayer());
Bukkit.getPluginManager().callEvent(event); Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return false;
}
if (event.isCancelled()) {
return false;
}
}
skillCasting.close(); skillCasting.close();
this.skillCasting = null; this.skillCasting = null;
setLastActivity(PlayerActivity.ACTION_BAR_MESSAGE, 0); // Reset action bar setLastActivity(PlayerActivity.ACTION_BAR_MESSAGE, 0); // Reset action bar

View File

@ -1,9 +1,10 @@
package net.Indyuce.mmocore.api.player.attribute; package net.Indyuce.mmocore.api.player.attribute;
import io.lumine.mythic.lib.api.stat.StatMap; import io.lumine.mythic.lib.api.stat.StatInstance;
import io.lumine.mythic.lib.api.stat.handler.StatHandler; import io.lumine.mythic.lib.api.stat.handler.StatHandler;
import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.PlayerData;
import org.bukkit.configuration.ConfigurationSection;
/** /**
* This fixes an issue where registering new stat modifiers in ML * This fixes an issue where registering new stat modifiers in ML
@ -13,17 +14,13 @@ import net.Indyuce.mmocore.api.player.PlayerData;
* This stat handler MAY call subsequent stat handlers. There might * This stat handler MAY call subsequent stat handlers. There might
* be infinite recursion problems if another attr. grants extra attribute pts. * be infinite recursion problems if another attr. grants extra attribute pts.
*/ */
public class MMOCoreAttributeStatHandler implements StatHandler { public class MMOCoreAttributeStatHandler extends StatHandler {
private final PlayerAttribute attr; private final PlayerAttribute attr;
private final String statName;
public MMOCoreAttributeStatHandler(PlayerAttribute attr) { public MMOCoreAttributeStatHandler(ConfigurationSection config, PlayerAttribute attr) {
super(config, "ADDITIONAL_" + attr.getId().toUpperCase().replace("-", "_"));
this.attr = attr; this.attr = attr;
this.statName = "ADDITIONAL_" + attr.getId().toUpperCase().replace("-", "_");
}
public String getStat() {
return statName;
} }
/** /**
@ -31,22 +28,12 @@ public class MMOCoreAttributeStatHandler implements StatHandler {
* is not loaded yet, hence the try/catch clause * is not loaded yet, hence the try/catch clause
*/ */
@Override @Override
public void runUpdate(StatMap statMap) { public void runUpdate(StatInstance instance) {
try { try {
final PlayerData playerData = MMOCore.plugin.dataProvider.getDataManager().get(statMap.getPlayerData().getUniqueId()); final PlayerData playerData = MMOCore.plugin.dataProvider.getDataManager().get(instance.getMap().getPlayerData().getUniqueId());
playerData.getAttributes().getInstance(attr).updateStats(); playerData.getAttributes().getInstance(attr).updateStats();
} catch (NullPointerException exception) { } catch (NullPointerException exception) {
// Player data is not loaded yet so there's nothing to update. // Player data is not loaded yet so there's nothing to update.
} }
} }
@Override
public double getBaseValue(StatMap statMap) {
return 0;
}
@Override
public double getTotalValue(StatMap statMap) {
return statMap.getStat(statName);
}
} }

View File

@ -222,7 +222,7 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject {
for (String key : config.getStringList("main-exp-sources")) for (String key : config.getStringList("main-exp-sources"))
try { try {
MMOCore.plugin.experience.registerSource(MMOCore.plugin.loadManager.loadExperienceSource(new MMOLineConfig(key), this)); MMOCore.plugin.experience.registerSource(MMOCore.plugin.loadManager.loadExperienceSource(new MMOLineConfig(key), this));
} catch (IllegalArgumentException exception) { } catch (RuntimeException exception) {
MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load exp source '" + key + "' from class '" MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load exp source '" + key + "' from class '"
+ id + "': " + exception.getMessage()); + id + "': " + exception.getMessage());
} }

View File

@ -39,8 +39,14 @@ public class MMOCoreUtils {
: caseOnWords(item.getType().name().replace("_", " ")); : caseOnWords(item.getType().name().replace("_", " "));
} }
/**
* @param current Current value of resource
* @param maxStat Maximum value of resource
* @return Clamped resource value. If the provided current value is 0,
* this function will return the maximum resource value.
*/
public static double fixResource(double current, double maxStat) { public static double fixResource(double current, double maxStat) {
return current == 0 ? maxStat : Math.min(current, maxStat); return current == 0 ? maxStat : Math.max(0, Math.min(current, maxStat));
} }
public static String caseOnWords(String s) { public static String caseOnWords(String s) {
@ -62,7 +68,6 @@ public class MMOCoreUtils {
} }
/** /**
*
* @param value an integer you want to convert * @param value an integer you want to convert
* @return the string representing the integer but with roman letters * @return the string representing the integer but with roman letters
*/ */
@ -82,23 +87,25 @@ public class MMOCoreUtils {
roman_numerals.put("IV", 4); roman_numerals.put("IV", 4);
roman_numerals.put("I", 1); roman_numerals.put("I", 1);
String res = ""; String res = "";
for(Map.Entry<String, Integer> entry : roman_numerals.entrySet()){ for (Map.Entry<String, Integer> entry : roman_numerals.entrySet()) {
int matches = value/entry.getValue(); int matches = value / entry.getValue();
res += repeat(entry.getKey(), matches); res += repeat(entry.getKey(), matches);
value = value % entry.getValue(); value = value % entry.getValue();
} }
return res; return res;
} }
private static String repeat(String s, int n) { private static String repeat(String s, int n) {
if(s == null) { if (s == null) {
return null; return null;
} }
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
for(int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
sb.append(s); sb.append(s);
} }
return sb.toString(); return sb.toString();
} }
/** /**
* Displays an in game indicator using a hologram. This uses * Displays an in game indicator using a hologram. This uses
* LumineUtils hologramFactory to summon holograms * LumineUtils hologramFactory to summon holograms
@ -260,8 +267,9 @@ public class MMOCoreUtils {
* @param damage Damage that needs to be applied * @param damage Damage that needs to be applied
*/ */
public static void decreaseDurability(Player player, EquipmentSlot slot, int damage) { public static void decreaseDurability(Player player, EquipmentSlot slot, int damage) {
ItemStack item = player.getInventory().getItem(slot); ItemStack item = player.getInventory().getItem(slot);
if (item == null || item.getType().getMaxDurability() == 0 || !item.hasItemMeta() || !(item.getItemMeta() instanceof Damageable) || item.getItemMeta().isUnbreakable()) if (item == null || item.getType().getMaxDurability() == 0 || item.getItemMeta().isUnbreakable())
return; return;
PlayerItemDamageEvent event = new PlayerItemDamageEvent(player, item, damage); PlayerItemDamageEvent event = new PlayerItemDamageEvent(player, item, damage);
@ -270,11 +278,12 @@ public class MMOCoreUtils {
return; return;
ItemMeta meta = item.getItemMeta(); ItemMeta meta = item.getItemMeta();
if (event.getDamage() + ((Damageable) meta).getDamage() >= item.getType().getMaxDurability()) { final int newDamage = event.getDamage() + ((Damageable) meta).getDamage();
if (newDamage >= item.getType().getMaxDurability()) {
player.playSound(player.getLocation(), Sound.ENTITY_ITEM_BREAK, 1F, 1F); player.playSound(player.getLocation(), Sound.ENTITY_ITEM_BREAK, 1F, 1F);
player.getInventory().setItem(slot, null); player.getInventory().setItem(slot, null);
} else { } else {
((Damageable) meta).setDamage(((Damageable) meta).getDamage() + event.getDamage()); ((Damageable) meta).setDamage(newDamage);
item.setItemMeta(meta); item.setItemMeta(meta);
} }
} }

View File

@ -2,7 +2,8 @@ package net.Indyuce.mmocore.experience.source;
import io.lumine.mythic.lib.MythicLib; import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.MMOLineConfig; import io.lumine.mythic.lib.api.MMOLineConfig;
import io.lumine.mythic.lib.api.event.PlayerKillEntityEvent; import io.lumine.mythic.lib.api.event.PlayerAttackEvent;
import io.lumine.mythic.lib.util.FlushableRegistry;
import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.util.MMOCoreUtils; import net.Indyuce.mmocore.api.util.MMOCoreUtils;
@ -15,9 +16,11 @@ import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.persistence.PersistentDataType; import org.bukkit.persistence.PersistentDataType;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.UUID;
public class KillMobExperienceSource extends SpecificExperienceSource<Entity> { public class KillMobExperienceSource extends SpecificExperienceSource<Entity> {
private final EntityType type; private final EntityType type;
@ -37,17 +40,36 @@ public class KillMobExperienceSource extends SpecificExperienceSource<Entity> {
public ExperienceSourceManager<KillMobExperienceSource> newManager() { public ExperienceSourceManager<KillMobExperienceSource> newManager() {
return new ExperienceSourceManager<KillMobExperienceSource>() { return new ExperienceSourceManager<KillMobExperienceSource>() {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) /**
public void a(PlayerKillEntityEvent event) { * This map is used to keep track of the last player who
Bukkit.getScheduler().runTaskLater(MMOCore.plugin, () -> { * hit some entity. It is flushed on entity death.
if (event.getTarget().isDead() && !event.getTarget().getPersistentDataContainer().has(new NamespacedKey(MMOCore.plugin, "spawner_spawned"), PersistentDataType.STRING)) { */
PlayerData data = PlayerData.get(event.getPlayer()); private final FlushableRegistry<UUID, UUID> registry = new FlushableRegistry<>((entity, attacker) -> Bukkit.getEntity(entity) == null, 20 * 60);
for (KillMobExperienceSource source : getSources()) @Override
if (source.matches(data, event.getTarget())) public void whenClosed() {
source.giveExperience(data, 1, MMOCoreUtils.getCenterLocation(event.getTarget())); registry.close();
} }
}, 2);
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void registerLastAttacker(PlayerAttackEvent event) {
registry.getRegistry().put(event.getEntity().getUniqueId(), event.getAttacker().getData().getUniqueId());
}
@EventHandler(priority = EventPriority.MONITOR)
public void giveExp(EntityDeathEvent event) {
// Always remove entry from map
final @Nullable UUID lastAttacker = this.registry.getRegistry().remove(event.getEntity().getUniqueId());
if (lastAttacker == null) return;
if (event.getEntity().getPersistentDataContainer().has(new NamespacedKey(MMOCore.plugin, "spawner_spawned"), PersistentDataType.STRING))
return;
final PlayerData data = PlayerData.get(lastAttacker);
for (KillMobExperienceSource source : getSources())
if (source.matches(data, event.getEntity()))
source.giveExperience(data, 1, MMOCoreUtils.getCenterLocation(event.getEntity()));
} }
}; };
} }

View File

@ -18,8 +18,7 @@ public class BiomeCondition extends Condition {
} }
@Override @Override
public boolean isMet(ConditionInstance entity) { public boolean isMet(ConditionInstance instance) {
Biome currentBiome = entity.getEntity().getLocation().getBlock().getBiome(); return names.contains(instance.getLocation().getBlock().getBiome().name());
return names.contains(currentBiome.name());
} }
} }

View File

@ -6,17 +6,18 @@ import java.util.stream.Stream;
import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.MMOCore;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
public class ConditionInstance { public class ConditionInstance {
private final Entity entity; private final Entity entity;
private final Location applied; private final Location applied;
private final List<String> regions; private final List<String> regions;
public ConditionInstance(Entity entity) { public ConditionInstance(@NotNull Entity entity) {
this(entity, entity.getLocation()); this(entity, entity.getLocation());
} }
public ConditionInstance(Entity entity, Location applied) { public ConditionInstance(@NotNull Entity entity, @NotNull Location applied) {
this.entity = entity; this.entity = entity;
this.regions = MMOCore.plugin.regionHandler.getRegions(this.applied = applied); this.regions = MMOCore.plugin.regionHandler.getRegions(this.applied = applied);
@ -27,10 +28,17 @@ public class ConditionInstance {
return regions.contains(name); return regions.contains(name);
} }
@Deprecated
public Location getAppliedLocation() { public Location getAppliedLocation() {
return applied; return applied;
} }
@NotNull
public Location getLocation() {
return applied;
}
@NotNull
public Entity getEntity() { public Entity getEntity() {
return entity; return entity;
} }

View File

@ -6,7 +6,7 @@ import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
public class DistanceCondition extends Condition{ public class DistanceCondition extends Condition {
private final Location location; private final Location location;
private final double distance; private final double distance;
@ -17,18 +17,14 @@ public class DistanceCondition extends Condition{
Validate.isTrue(config.contains("y")); Validate.isTrue(config.contains("y"));
Validate.isTrue(config.contains("z")); Validate.isTrue(config.contains("z"));
Validate.isTrue(config.contains("distance")); Validate.isTrue(config.contains("distance"));
Validate.isTrue(Bukkit.getWorld(config.getString("world"))!=null,"This world doesn't exist"); Validate.isTrue(Bukkit.getWorld(config.getString("world")) != null, "This world doesn't exist");
location=new Location(Bukkit.getWorld(config.getString("world")),config.getDouble("x"), location = new Location(Bukkit.getWorld(config.getString("world")), config.getDouble("x"),
config.getDouble("y"),config.getDouble("z")); config.getDouble("y"), config.getDouble("z"));
distance=config.getDouble("distance"); distance = config.getDouble("distance");
} }
@Override @Override
public boolean isMet(ConditionInstance entity) { public boolean isMet(ConditionInstance instance) {
Entity entity1=entity.getEntity(); return instance.getLocation().getWorld().equals(location.getWorld()) && location.distance(instance.getLocation()) < distance;
return entity1.getWorld().equals(location.getWorld())&&location.distance(entity1.getLocation())<distance;
} }
} }

View File

@ -21,8 +21,7 @@ public class TimeCondition extends Condition {
@Override @Override
public boolean isMet(ConditionInstance entity) { public boolean isMet(ConditionInstance entity) {
if (entity.getEntity() instanceof Player player) { long time = entity.getLocation().getWorld().getTime();
long time = player.getWorld().getTime();
if (min < max) { if (min < max) {
return time > min && time < max; return time > min && time < max;
@ -31,7 +30,4 @@ public class TimeCondition extends Condition {
return time > min || time < max; return time > min || time < max;
} }
} }
return false;
}
} }

View File

@ -18,16 +18,14 @@ public class WeatherCondition extends Condition {
@Override @Override
public boolean isMet(ConditionInstance entity) { public boolean isMet(ConditionInstance entity) {
if (entity.getEntity() instanceof Player player) { boolean isClear = entity.getLocation().getWorld().isClearWeather();
boolean isClear = player.getWorld().isClearWeather(); boolean hasStorm = entity.getLocation().getWorld().hasStorm();
boolean hasStorm = player.getWorld().hasStorm();
if (condition.equalsIgnoreCase("clear")) { if (condition.equalsIgnoreCase("clear")) {
return isClear; return isClear;
} else if (condition.equalsIgnoreCase("stormy")) { } else if (condition.equalsIgnoreCase("stormy")) {
return hasStorm; return hasStorm;
} }
}
return false; return false;
} }

View File

@ -17,6 +17,6 @@ public class WorldCondition extends Condition {
@Override @Override
public boolean isMet(ConditionInstance entity) { public boolean isMet(ConditionInstance entity) {
return names.contains(entity.getEntity().getWorld().getName()) || names.contains("__global__"); return names.contains(entity.getLocation().getWorld().getName()) || names.contains("__global__");
} }
} }

View File

@ -5,6 +5,7 @@ import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigFile; import net.Indyuce.mmocore.api.ConfigFile;
import net.Indyuce.mmocore.api.player.attribute.MMOCoreAttributeStatHandler; import net.Indyuce.mmocore.api.player.attribute.MMOCoreAttributeStatHandler;
import net.Indyuce.mmocore.api.player.attribute.PlayerAttribute; import net.Indyuce.mmocore.api.player.attribute.PlayerAttribute;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -46,8 +47,9 @@ public class AttributeManager implements MMOCoreManager {
MMOCore.log(Level.WARNING, "Could not load attribute '" + key + "': " + exception.getMessage()); MMOCore.log(Level.WARNING, "Could not load attribute '" + key + "': " + exception.getMessage());
} }
final ConfigurationSection statsConfig = new ConfigFile(MythicLib.plugin, "", "stats").getConfig();
for (PlayerAttribute attr : getAll()) { for (PlayerAttribute attr : getAll()) {
final MMOCoreAttributeStatHandler handler = new MMOCoreAttributeStatHandler(attr); final MMOCoreAttributeStatHandler handler = new MMOCoreAttributeStatHandler(statsConfig, attr);
MythicLib.plugin.getStats().registerStat(handler.getStat(), handler); MythicLib.plugin.getStats().registerStat(handler.getStat(), handler);
MythicLib.plugin.getStats().registerStat(handler.getStat() + "_PERCENT", handler); MythicLib.plugin.getStats().registerStat(handler.getStat() + "_PERCENT", handler);
} }

View File

@ -9,7 +9,7 @@ import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.File; import java.io.File;
@ -25,10 +25,8 @@ public class ExperienceManager implements MMOCoreManager {
* Experience sources from the exp-sources.yml config file where you can * Experience sources from the exp-sources.yml config file where you can
* input any exp source which can later be used along with the 'from' * input any exp source which can later be used along with the 'from'
* exp source anywhere in the plugin. * exp source anywhere in the plugin.
* <p>
* TODO First needs to edit the exp-source current structure. This is going to break a lot of things
* *
* @deprecated See TODO * @deprecated TODO First needs to edit the exp-source current structure. This is going to break a lot of things
*/ */
@Deprecated @Deprecated
private final Map<String, List<ExperienceSource<?>>> publicExpSources = new HashMap<>(); private final Map<String, List<ExperienceSource<?>>> publicExpSources = new HashMap<>();
@ -55,6 +53,7 @@ public class ExperienceManager implements MMOCoreManager {
return expCurves.containsKey(id); return expCurves.containsKey(id);
} }
@NotNull
public ExpCurve getCurveOrThrow(String id) { public ExpCurve getCurveOrThrow(String id) {
Validate.isTrue(hasCurve(id), "Could not find exp curve with ID '" + id + "'"); Validate.isTrue(hasCurve(id), "Could not find exp curve with ID '" + id + "'");
return expCurves.get(id); return expCurves.get(id);
@ -70,10 +69,12 @@ public class ExperienceManager implements MMOCoreManager {
return expTables.containsKey(id); return expTables.containsKey(id);
} }
@NotNull
public ExperienceTable getTableOrThrow(String id) { public ExperienceTable getTableOrThrow(String id) {
return Objects.requireNonNull(expTables.get(id), "Could not find exp table with ID '" + id + "'"); return Objects.requireNonNull(expTables.get(id), "Could not find exp table with ID '" + id + "'");
} }
@NotNull
public ExperienceTable loadExperienceTable(Object obj) { public ExperienceTable loadExperienceTable(Object obj) {
if (obj instanceof ConfigurationSection) if (obj instanceof ConfigurationSection)
@ -99,7 +100,7 @@ public class ExperienceManager implements MMOCoreManager {
expCurves.clear(); expCurves.clear();
expTables.clear(); expTables.clear();
managers.values().forEach(HandlerList::unregisterAll); managers.forEach((c, manager) -> manager.close());
managers.clear(); managers.clear();
} }

View File

@ -115,6 +115,7 @@ public class RestrictionManager implements MMOCoreManager {
String parentFormat = formatId(config.getString("parent")); String parentFormat = formatId(config.getString("parent"));
parent = Objects.requireNonNull(map.get(parentFormat), "Could not find parent with ID '" + parentFormat + "'"); parent = Objects.requireNonNull(map.get(parentFormat), "Could not find parent with ID '" + parentFormat + "'");
} }
if (config.contains("can-mine"))
for (String key : config.getStringList("can-mine")) for (String key : config.getStringList("can-mine"))
mineable.add(MMOCore.plugin.loadManager.loadBlockType(new MMOLineConfig(key)).generateKey()); mineable.add(MMOCore.plugin.loadManager.loadBlockType(new MMOLineConfig(key)).generateKey());
} }

View File

@ -78,15 +78,12 @@ public class MMOCoreDataSynchronizer extends SQLDataSynchronizer<PlayerData> {
getData().setUnlockedItems(unlockedItems); getData().setUnlockedItems(unlockedItems);
if (!isEmpty(result.getString("guild"))) { if (!isEmpty(result.getString("guild"))) {
final Guild guild = MMOCore.plugin.dataProvider.getGuildManager().getGuild(result.getString("guild")); final Guild guild = MMOCore.plugin.dataProvider.getGuildManager().getGuild(result.getString("guild"));
if (guild != null) if (guild != null) getData().setGuild(guild.hasMember(getData().getUniqueId()) ? guild : null);
getData().setGuild(guild.hasMember(getData().getUniqueId()) ? guild : null);
} }
if (!isEmpty(result.getString("attributes"))) if (!isEmpty(result.getString("attributes"))) getData().getAttributes().load(result.getString("attributes"));
getData().getAttributes().load(result.getString("attributes"));
if (!isEmpty(result.getString("professions"))) if (!isEmpty(result.getString("professions")))
getData().getCollectionSkills().load(result.getString("professions")); getData().getCollectionSkills().load(result.getString("professions"));
if (!isEmpty(result.getString("quests"))) if (!isEmpty(result.getString("quests"))) getData().getQuestData().load(result.getString("quests"));
getData().getQuestData().load(result.getString("quests"));
getData().getQuestData().updateBossBar(); getData().getQuestData().updateBossBar();
if (!isEmpty(result.getString("waypoints"))) if (!isEmpty(result.getString("waypoints")))
getData().getWaypoints().addAll(MMOCoreUtils.jsonArrayToList(result.getString("waypoints"))); getData().getWaypoints().addAll(MMOCoreUtils.jsonArrayToList(result.getString("waypoints")));
@ -101,8 +98,7 @@ public class MMOCoreDataSynchronizer extends SQLDataSynchronizer<PlayerData> {
JsonObject object = MythicLib.plugin.getGson().fromJson(result.getString("bound_skills"), JsonObject.class); JsonObject object = MythicLib.plugin.getGson().fromJson(result.getString("bound_skills"), JsonObject.class);
for (Map.Entry<String, JsonElement> entry : object.entrySet()) { for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
ClassSkill skill = getData().getProfess().getSkill(entry.getValue().getAsString()); ClassSkill skill = getData().getProfess().getSkill(entry.getValue().getAsString());
if (skill != null) if (skill != null) getData().bindSkill(Integer.parseInt(entry.getKey()), skill);
getData().bindSkill(Integer.parseInt(entry.getKey()), skill);
} }
} }
@ -123,15 +119,17 @@ public class MMOCoreDataSynchronizer extends SQLDataSynchronizer<PlayerData> {
* These should be loaded after to make sure that the * These should be loaded after to make sure that the
* MAX_MANA, MAX_STAMINA & MAX_STELLIUM stats are already loaded. * MAX_MANA, MAX_STAMINA & MAX_STELLIUM stats are already loaded.
*/ */
double health = result.getDouble("health");
getData().setMana(result.getDouble("mana")); getData().setMana(result.getDouble("mana"));
getData().setStamina(result.getDouble("stamina")); getData().setStamina(result.getDouble("stamina"));
getData().setStellium(result.getDouble("stellium")); getData().setStellium(result.getDouble("stellium"));
if (getData().isOnline()) { if (getData().isOnline() && !getData().getPlayer().isDead()) {
//If the player is not dead and the health is 0, this means that the data was
//missing from the data base and it gives full health to the player. /*
health = health == 0 && !getData().getPlayer().isDead() ? getData().getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue() : health; * If the player is not dead and the health is 0, this means that the data was
health = Math.max(Math.min(health, getData().getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue()), 0); * missing from the data base and it gives full health to the player. If the
* player is dead however, it must not account for that subtle edge case.
*/
final double health = MMOCoreUtils.fixResource(result.getDouble("health"), getData().getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue());
getData().getPlayer().setHealth(health); getData().getPlayer().setHealth(health);
} }

View File

@ -134,7 +134,7 @@ public class YAMLPlayerDataHandler extends YAMLSynchronizedDataHandler<PlayerDat
data.setStamina(config.contains("stamina") ? config.getDouble("stamina") : data.getStats().getStat("MAX_STAMINA")); data.setStamina(config.contains("stamina") ? config.getDouble("stamina") : data.getStats().getStat("MAX_STAMINA"));
data.setStellium(config.contains("stellium") ? config.getDouble("stellium") : data.getStats().getStat("MAX_STELLIUM")); data.setStellium(config.contains("stellium") ? config.getDouble("stellium") : data.getStats().getStat("MAX_STELLIUM"));
if (data.isOnline()) if (data.isOnline() && !data.getPlayer().isDead())
data.getPlayer().setHealth(MMOCoreUtils.fixResource(config.getDouble("health"), data.getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue())); data.getPlayer().setHealth(MMOCoreUtils.fixResource(config.getDouble("health"), data.getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue()));
} }

View File

@ -1,19 +1,23 @@
package net.Indyuce.mmocore.manager.profession; package net.Indyuce.mmocore.manager.profession;
import io.lumine.mythic.lib.util.Closeable;
import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.experience.source.type.ExperienceSource; import net.Indyuce.mmocore.experience.source.type.ExperienceSource;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
public abstract class ExperienceSourceManager<T extends ExperienceSource> implements Listener { public abstract class ExperienceSourceManager<T extends ExperienceSource> implements Listener, Closeable {
/** /**
* List of all active experience sources * List of all active experience sources
*/ */
private final Set<T> sources = new HashSet<>(); private final Set<T> sources = new HashSet<>();
private boolean open = true;
public ExperienceSourceManager() { public ExperienceSourceManager() {
Bukkit.getPluginManager().registerEvents(this, MMOCore.plugin); Bukkit.getPluginManager().registerEvents(this, MMOCore.plugin);
@ -23,6 +27,19 @@ public abstract class ExperienceSourceManager<T extends ExperienceSource> implem
sources.add(source); sources.add(source);
} }
public void whenClosed() {
// Nothing by default
}
@Override
public void close() {
Validate.isTrue(open, "Manager is already closed");
open = false;
HandlerList.unregisterAll(this);
whenClosed();
}
public Set<T> getSources() { public Set<T> getSources() {
return sources; return sources;
} }

View File

@ -10,6 +10,9 @@ import net.Indyuce.mmocore.loot.fishing.FishingDropItem;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.FishHook;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.*; import java.util.*;
import java.util.logging.Level; import java.util.logging.Level;
@ -36,8 +39,9 @@ public class FishingManager extends SpecificProfessionManager {
MMOCore.plugin.statManager.registerProfession("CRITICAL_FISHING_FAILURE_CHANCE", getLinkedProfession()); MMOCore.plugin.statManager.registerProfession("CRITICAL_FISHING_FAILURE_CHANCE", getLinkedProfession());
} }
public FishingDropTable calculateDropTable(Entity entity) { @NotNull
ConditionInstance conditionEntity = new ConditionInstance(entity); public FishingDropTable calculateDropTable(@NotNull Player player, @NotNull FishHook hook) {
ConditionInstance conditionEntity = new ConditionInstance(player, hook.getLocation());
for (FishingDropTable table : tables) for (FishingDropTable table : tables)
if (table.areConditionsMet(conditionEntity)) if (table.areConditionsMet(conditionEntity))

View File

@ -16,7 +16,6 @@ import org.bukkit.GameMode;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -74,6 +73,9 @@ public class KeyCombos implements SkillCastingListener {
if (player.getGameMode() == GameMode.CREATIVE && !MMOCore.plugin.configManager.canCreativeCast) if (player.getGameMode() == GameMode.CREATIVE && !MMOCore.plugin.configManager.canCreativeCast)
return; return;
// Don't start combos if no skills are bound
if (playerData.getBoundSkills().isEmpty()) return;
// Start combo when there is an initializer key // Start combo when there is an initializer key
if (!event.getData().isCasting() && initializerKey != null) { if (!event.getData().isCasting() && initializerKey != null) {
if (event.getPressed() == initializerKey) { if (event.getPressed() == initializerKey) {
@ -82,7 +84,7 @@ public class KeyCombos implements SkillCastingListener {
if (event.getPressed().shouldCancelEvent()) event.setCancelled(true); if (event.getPressed().shouldCancelEvent()) event.setCancelled(true);
// Start combo // Start combo
if (playerData.setSkillCasting(new CustomSkillCastingInstance(playerData)) && beginComboSound != null) if (playerData.setSkillCasting() && beginComboSound != null)
beginComboSound.playTo(player); beginComboSound.playTo(player);
} }
@ -97,10 +99,9 @@ public class KeyCombos implements SkillCastingListener {
// Start combo when there is NO initializer key // Start combo when there is NO initializer key
else { else {
final @NotNull ComboMap comboMap = Objects.requireNonNullElse(playerData.getProfess().getComboMap(), this.comboMap); final @NotNull ComboMap comboMap = Objects.requireNonNullElse(playerData.getProfess().getComboMap(), this.comboMap);
if (comboMap.isComboStart(event.getPressed())) { if (comboMap.isComboStart(event.getPressed()) && playerData.setSkillCasting()) {
casting = new CustomSkillCastingInstance(playerData); casting = (CustomSkillCastingInstance) playerData.getSkillCasting();
if (playerData.setSkillCasting(new CustomSkillCastingInstance(playerData)) && beginComboSound != null) if (beginComboSound != null) beginComboSound.playTo(player);
beginComboSound.playTo(player);
} }
} }
@ -174,7 +175,8 @@ public class KeyCombos implements SkillCastingListener {
@Override @Override
public void onTick() { public void onTick() {
if (actionBarOptions != null) if (actionBarOptions.isSubtitle) if (getCaster().getBoundSkills().isEmpty()) close();
else if (actionBarOptions != null) if (actionBarOptions.isSubtitle)
getCaster().getPlayer().sendTitle(" ", actionBarOptions.format(this), 0, 20, 0); getCaster().getPlayer().sendTitle(" ", actionBarOptions.format(this), 0, 20, 0);
else getCaster().displayActionBar(actionBarOptions.format(this)); else getCaster().displayActionBar(actionBarOptions.format(this));
} }

View File

@ -55,12 +55,10 @@ public class SkillBar implements SkillCastingListener {
// Enter spell casting // Enter spell casting
final PlayerData playerData = event.getData(); final PlayerData playerData = event.getData();
if (player.getGameMode() != GameMode.SPECTATOR && (MMOCore.plugin.configManager.canCreativeCast || player.getGameMode() != GameMode.CREATIVE) && !playerData.isCasting() && !playerData.getBoundSkills().isEmpty()) { if (player.getGameMode() != GameMode.SPECTATOR && (MMOCore.plugin.configManager.canCreativeCast || player.getGameMode() != GameMode.CREATIVE) && !playerData.isCasting() && !playerData.getBoundSkills().isEmpty())
if (playerData.setSkillCasting(new CustomSkillCastingInstance(playerData))) { if (playerData.setSkillCasting())
MMOCore.plugin.soundManager.getSound(SoundEvent.SPELL_CAST_BEGIN).playTo(player); MMOCore.plugin.soundManager.getSound(SoundEvent.SPELL_CAST_BEGIN).playTo(player);
} }
}
}
public class CustomSkillCastingInstance extends SkillCastingInstance { public class CustomSkillCastingInstance extends SkillCastingInstance {
private final String ready = MMOCore.plugin.configManager.getSimpleMessage("casting.action-bar.ready").message(); private final String ready = MMOCore.plugin.configManager.getSimpleMessage("casting.action-bar.ready").message();

View File

@ -60,6 +60,7 @@ public class SkillScroller implements SkillCastingListener {
Player player = playerData.getPlayer(); Player player = playerData.getPlayer();
if (player.getGameMode() == GameMode.CREATIVE && !MMOCore.plugin.configManager.canCreativeCast) if (player.getGameMode() == GameMode.CREATIVE && !MMOCore.plugin.configManager.canCreativeCast)
return; return;
if (event.getPressed() == enterKey) { if (event.getPressed() == enterKey) {
// Leave casting mode // Leave casting mode
@ -80,7 +81,7 @@ public class SkillScroller implements SkillCastingListener {
if (event.getPressed().shouldCancelEvent()) event.setCancelled(true); if (event.getPressed().shouldCancelEvent()) event.setCancelled(true);
// Enter casting mode // Enter casting mode
if (!playerData.setSkillCasting(new CustomSkillCastingInstance(playerData))) { if (!playerData.setSkillCasting()) {
return; return;
} }

View File

@ -53,7 +53,7 @@ public class Ambers extends SkillHandler<SimpleSkillResult> implements Listener
if (passive == null) if (passive == null)
return; return;
passive.getTriggeredSkill().cast(new TriggerMetadata(event.getAttacker(), event.getAttack(), event.getEntity())); passive.getTriggeredSkill().cast(new TriggerMetadata(event));
} }
public static class Amber extends BukkitRunnable { public static class Amber extends BukkitRunnable {

View File

@ -45,6 +45,6 @@ public class Sneaky_Picky extends SkillHandler<SimpleSkillResult> implements Lis
if (skill == null) if (skill == null)
return; return;
skill.getTriggeredSkill().cast(new TriggerMetadata(event.getAttacker(), event.getAttack(), event.getEntity())); skill.getTriggeredSkill().cast(new TriggerMetadata(event));
} }
} }

View File

@ -45,7 +45,7 @@ public class FishingListener implements Listener {
* Checks for drop tables. If no drop table, just plain vanilla * Checks for drop tables. If no drop table, just plain vanilla
* fishing OTHERWISE initialize fishing, register other listener. * fishing OTHERWISE initialize fishing, register other listener.
*/ */
FishingDropTable table = MMOCore.plugin.fishingManager.calculateDropTable(player); FishingDropTable table = MMOCore.plugin.fishingManager.calculateDropTable(player, hook);
if (table == null) if (table == null)
return; return;

View File

@ -24,6 +24,9 @@ permissions:
mmocore.currency: mmocore.currency:
description: Access to /deposit and /withdraw description: Access to /deposit and /withdraw
default: op default: op
mmocore.bypass-waypoint-wait:
description: Bypass waypoint waiting time
default: op
mmocore.class-select: mmocore.class-select:
description: Access to /class description: Access to /class
default: op default: op