diff --git a/src/main/java/net/Indyuce/mmocore/api/load/DefaultMMOLoader.java b/src/main/java/net/Indyuce/mmocore/api/load/DefaultMMOLoader.java index 7b8857b7..074b123d 100644 --- a/src/main/java/net/Indyuce/mmocore/api/load/DefaultMMOLoader.java +++ b/src/main/java/net/Indyuce/mmocore/api/load/DefaultMMOLoader.java @@ -1,16 +1,12 @@ package net.Indyuce.mmocore.api.load; import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser; +import net.Indyuce.mmocore.loot.droptable.condition.*; import org.bukkit.configuration.ConfigurationSection; import net.Indyuce.mmocore.api.block.BlockType; import net.Indyuce.mmocore.api.block.SkullBlockType; import net.Indyuce.mmocore.api.block.VanillaBlockType; -import net.Indyuce.mmocore.loot.droptable.condition.BiomeCondition; -import net.Indyuce.mmocore.loot.droptable.condition.Condition; -import net.Indyuce.mmocore.loot.droptable.condition.LevelCondition; -import net.Indyuce.mmocore.loot.droptable.condition.PermissionCondition; -import net.Indyuce.mmocore.loot.droptable.condition.WorldCondition; import net.Indyuce.mmocore.loot.droptable.dropitem.DropItem; import net.Indyuce.mmocore.loot.droptable.dropitem.DropTableDropItem; import net.Indyuce.mmocore.loot.droptable.dropitem.GoldDropItem; @@ -109,6 +105,9 @@ public class DefaultMMOLoader extends MMOLoader { @Override public Condition loadCondition(MMOLineConfig config) { + if(config.getKey().equals("distance")) + return new DistanceCondition(config); + if (config.getKey().equals("world")) return new WorldCondition(config); diff --git a/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java b/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java index dc9b8359..75c9d6bc 100644 --- a/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java +++ b/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java @@ -459,7 +459,7 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc * * @param target Target waypoint */ - public void warp(Waypoint source, Waypoint target, CostType costType) { + public void warp(Waypoint target, double cost) { /* * This cooldown is only used internally to make sure the player is not @@ -467,10 +467,6 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc * player waypoints data */ setLastActivity(PlayerActivity.USE_WAYPOINT); - Validate.isTrue(source!=null||costType!=CostType.NORMAL_USE,"You must precise a source to use normal waypoint" ); - final double cost = costType == CostType.DYNAMIC_USE ? target.getDynamicCost() : source.getCost(target); - - giveStellium(-cost, PlayerResourceUpdateEvent.UpdateReason.USE_WAYPOINT); new BukkitRunnable() { diff --git a/src/main/java/net/Indyuce/mmocore/api/player/stats/StatType.java b/src/main/java/net/Indyuce/mmocore/api/player/stats/StatType.java index cfc69000..9f66f9bf 100644 --- a/src/main/java/net/Indyuce/mmocore/api/player/stats/StatType.java +++ b/src/main/java/net/Indyuce/mmocore/api/player/stats/StatType.java @@ -61,6 +61,7 @@ public enum StatType { // Utility ADDITIONAL_EXPERIENCE, COOLDOWN_REDUCTION, + CHANCE, // Damage-type based stats MAGIC_DAMAGE, diff --git a/src/main/java/net/Indyuce/mmocore/gui/WaypointViewer.java b/src/main/java/net/Indyuce/mmocore/gui/WaypointViewer.java index f913b127..5bfa25f6 100644 --- a/src/main/java/net/Indyuce/mmocore/gui/WaypointViewer.java +++ b/src/main/java/net/Indyuce/mmocore/gui/WaypointViewer.java @@ -12,6 +12,7 @@ import net.Indyuce.mmocore.waypoint.CostType; import net.Indyuce.mmocore.waypoint.Waypoint; import net.Indyuce.mmocore.waypoint.WaypointOption; import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.configuration.ConfigurationSection; @@ -21,10 +22,7 @@ import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; public class WaypointViewer extends EditableInventory { public WaypointViewer() { @@ -67,8 +65,9 @@ public class WaypointViewer extends EditableInventory { } public class WaypointItem extends SimplePlaceholderItem { - private final SimplePlaceholderItem noWaypoint, locked,notLinked,notDynamic; - private final WaypointItemHandler availWaypoint,noStellium; + private final SimplePlaceholderItem noWaypoint, locked; + private final WaypointItemHandler availWaypoint, noStellium, notLinked, notDynamic, currentWayPoint; + public WaypointItem(ConfigurationSection config) { super(Material.BARRIER, config); @@ -77,15 +76,18 @@ public class WaypointViewer extends EditableInventory { Validate.notNull(config.getConfigurationSection("locked"), "Could not load 'locked' config"); Validate.notNull(config.getConfigurationSection("not-a-destination"), "Could not load 'not-a-destination' config"); Validate.notNull(config.getConfigurationSection("not-dynamic"), "Could not load 'not-dynamic' config"); + Validate.notNull(config.getConfigurationSection("current-waypoint"), "Could not load 'current-waypoint' config"); Validate.notNull(config.getConfigurationSection("not-enough-stellium"), "Could not load 'not-enough-stellium' config"); Validate.notNull(config.getConfigurationSection("display"), "Could not load 'display' config"); + noWaypoint = new SimplePlaceholderItem(config.getConfigurationSection("no-waypoint")); locked = new SimplePlaceholderItem(config.getConfigurationSection("locked")); - notLinked = new SimplePlaceholderItem(config.getConfigurationSection("not-a-destination")); - notDynamic = new SimplePlaceholderItem(config.getConfigurationSection("not-dynamic")); - noStellium = new WaypointItemHandler(config.getConfigurationSection("not-enough-stellium")); - availWaypoint = new WaypointItemHandler(config.getConfigurationSection("display")); + notLinked = new WaypointItemHandler(config.getConfigurationSection("not-a-destination"), true); + notDynamic = new WaypointItemHandler(config.getConfigurationSection("not-dynamic"), true); + currentWayPoint = new WaypointItemHandler(config.getConfigurationSection("current-waypoint"), true); + noStellium = new WaypointItemHandler(config.getConfigurationSection("not-enough-stellium"), false); + availWaypoint = new WaypointItemHandler(config.getConfigurationSection("display"), false); } @Override @@ -102,6 +104,10 @@ public class WaypointViewer extends EditableInventory { // Locked waypoint? Waypoint waypoint = inv.waypoints.get(index); + if (inv.current != null && inv.current.equals(waypoint)) + return currentWayPoint.display(inv, n); + + if (!inv.getPlayerData().hasWaypoint(waypoint)) return locked.display(inv, n); @@ -109,17 +115,13 @@ public class WaypointViewer extends EditableInventory { if (inv.current != null && !inv.paths.containsKey(waypoint)) return notLinked.display(inv, n); + // Not dynamic waypoint - if (inv.current == null && !waypoint.hasOption(WaypointOption.DYNAMIC)) + if (inv.current == null && !inv.paths.containsKey(waypoint)) return notDynamic.display(inv, n); - - //Dynamic waypoint - if (waypoint.hasOption(WaypointOption.DYNAMIC) && inv.current == null && waypoint.getDynamicCost() > inv.getPlayerData().getStellium()) - return noStellium.display(inv, n); - //Normal cost - if (inv.current != null && inv.paths.get(waypoint).getCost() > inv.getPlayerData().getStellium()) + if (inv.paths.get(waypoint).getCost() > inv.getPlayerData().getStellium()) return noStellium.display(inv, n); @@ -128,8 +130,11 @@ public class WaypointViewer extends EditableInventory { } public class WaypointItemHandler extends InventoryItem { - public WaypointItemHandler(ConfigurationSection config) { + private final boolean onlyName; + + public WaypointItemHandler(ConfigurationSection config, boolean onlyName) { super(config); + this.onlyName = onlyName; } @Override @@ -151,10 +156,12 @@ public class WaypointViewer extends EditableInventory { Waypoint waypoint = inv.waypoints.get(inv.page * inv.getByFunction("waypoint").getSlots().size() + n); holders.register("name", waypoint.getName()); - holders.register("current_cost", decimal.format(inv.waypointCostType.equals(CostType.DYNAMIC_USE) ? waypoint.getDynamicCost() : inv.paths.get(waypoint).getCost())); - holders.register("normal_cost", decimal.format(inv.paths.get(waypoint).getCost())); - holders.register("dynamic_cost", decimal.format(waypoint.getDynamicCost())); - holders.register("intermediary_waypoints", inv.paths.get(waypoint).displayIntermediaryWayPoints()); + if (!onlyName) { + holders.register("current_cost",inv.paths.get(waypoint).getCost()); + holders.register("normal_cost", decimal.format(inv.paths.containsKey(waypoint) ? inv.paths.get(waypoint).getCost() : Double.POSITIVE_INFINITY)); + holders.register("dynamic_cost", decimal.format(waypoint.getDynamicCost())); + holders.register("intermediary_waypoints", inv.paths.containsKey(waypoint) ? inv.paths.get(waypoint).displayIntermediaryWayPoints(inv.waypointCostType.equals(CostType.DYNAMIC_USE)) : "none"); + } return holders; } @@ -176,6 +183,23 @@ public class WaypointViewer extends EditableInventory { for (Waypoint.PathInfo pathInfo : current.getAllPath()) paths.put(pathInfo.getFinalWaypoint(), pathInfo); } + if (current == null) { + //We first check all the dynamic waypoints + for (Waypoint waypoint : waypoints) { + if (waypoint.canHaveDynamicUse(playerData.getPlayer())) + paths.put(waypoint,new Waypoint.PathInfo(waypoint, waypoint.getDynamicCost())); + } + //Iterate through all the dynamic points and find all the points it is linked to and the path + HashSet waypointSet = new HashSet<>(paths.keySet()); + for (Waypoint source : waypointSet) { + for (Waypoint.PathInfo target : source.getAllPath()) { + if (!paths.containsKey(target.getFinalWaypoint()) || paths.get(target.getFinalWaypoint()).getCost() > target.getCost()) { + paths.put(target.getFinalWaypoint(), target); + } + } + } + + } this.waypointCostType = current == null ? CostType.DYNAMIC_USE : CostType.NORMAL_USE; } @@ -201,7 +225,8 @@ public class WaypointViewer extends EditableInventory { if (item.getFunction().equals("waypoint")) { PersistentDataContainer container = event.getCurrentItem().getItemMeta().getPersistentDataContainer(); - String tag = container.get(new NamespacedKey(MMOCore.plugin, "wayPointId"), PersistentDataType.STRING); + String tag = container.has(new NamespacedKey(MMOCore.plugin, "waypointId"), PersistentDataType.STRING) ? + container.get(new NamespacedKey(MMOCore.plugin, "waypointId"), PersistentDataType.STRING) : ""; if (tag.equals("")) return; @@ -220,20 +245,20 @@ public class WaypointViewer extends EditableInventory { } // Waypoint does not have target as destination - if (current != null && current.getPath(waypoint)==null) { + if (current != null && current.getPath(waypoint) == null) { MMOCore.plugin.configManager.getSimpleMessage("cannot-teleport-to").send(player); return; } // Not dynamic waypoint - if (current == null && !waypoint.hasOption(WaypointOption.DYNAMIC)) { + if (current == null && !paths.containsKey(waypoint)) { MMOCore.plugin.configManager.getSimpleMessage("not-dynamic-waypoint").send(player); return; } // Stellium cost CostType costType = current == null ? CostType.DYNAMIC_USE : CostType.NORMAL_USE; - double withdraw = current == null ? waypoint.getDynamicCost() : paths.get(waypoint).getCost(); + double withdraw = paths.get(waypoint).getCost(); double left = withdraw - playerData.getStellium(); if (left > 0) { MMOCore.plugin.configManager.getSimpleMessage("not-enough-stellium", "more", decimal.format(left)).send(player); @@ -244,10 +269,7 @@ public class WaypointViewer extends EditableInventory { return; player.closeInventory(); - if (current == null) - playerData.warp(null, waypoint, costType); - else - playerData.warp(current, waypoint, costType); + playerData.warp(waypoint, withdraw); } } diff --git a/src/main/java/net/Indyuce/mmocore/loot/chest/LootChestRegion.java b/src/main/java/net/Indyuce/mmocore/loot/chest/LootChestRegion.java index ef7e8217..6e265a3b 100644 --- a/src/main/java/net/Indyuce/mmocore/loot/chest/LootChestRegion.java +++ b/src/main/java/net/Indyuce/mmocore/loot/chest/LootChestRegion.java @@ -4,6 +4,7 @@ import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.event.LootChestSpawnEvent; import net.Indyuce.mmocore.api.player.PlayerActivity; import net.Indyuce.mmocore.api.player.PlayerData; +import net.Indyuce.mmocore.api.player.stats.StatType; import net.Indyuce.mmocore.loot.LootBuilder; import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; @@ -85,7 +86,7 @@ public class LootChestRegion { player.setLastActivity(PlayerActivity.LOOT_CHEST_SPAWN); // First randomly determine the chest tier - ChestTier tier = rollTier(); + ChestTier tier = rollTier(player); // Find a random location, 20 trials max Location location = getRandomLocation(player.getPlayer().getLocation()); @@ -116,14 +117,22 @@ public class LootChestRegion { MMOCore.plugin.lootChests.register(lootChest); } - // TODO stat to increase chance to get higher tiers? - public ChestTier rollTier() { + public ChestTier rollTier(PlayerData player) { + double chance = player.getStats().getStat(StatType.CHANCE); - double s = 0; + //chance=0 ->the tier.chance remains the same + //chance ->+inf -> the tier.chance becomes the same for everyone, uniform law + //chance=8-> tierChance=sqrt(tierChance) + double sum = 0; for (ChestTier tier : tiers) { - if (random.nextDouble() < tier.chance / (1 - s)) + sum += Math.pow(tier.chance, 1 / Math.pow((1 + chance),1/3)); + } + + double s=0; + for (ChestTier tier : tiers) { + s+=Math.pow(tier.chance, 1 / Math.pow((1 + chance),1/3))/sum; + if (random.nextDouble() < s) return tier; - s += tier.chance; } return tiers.stream().findAny().orElse(null); diff --git a/src/main/java/net/Indyuce/mmocore/loot/droptable/condition/DistanceCondition.java b/src/main/java/net/Indyuce/mmocore/loot/droptable/condition/DistanceCondition.java new file mode 100644 index 00000000..486ce977 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/loot/droptable/condition/DistanceCondition.java @@ -0,0 +1,39 @@ +package net.Indyuce.mmocore.loot.droptable.condition; + +import io.lumine.mythic.lib.api.MMOLineConfig; +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.generator.WorldInfo; + + +import java.util.stream.Collectors; + +public class DistanceCondition extends Condition{ + private final Location location; + private final double distance; + + public DistanceCondition(MMOLineConfig config) { + super(config); + Validate.isTrue(config.contains("world")); + Validate.isTrue(config.contains("x")); + Validate.isTrue(config.contains("y")); + Validate.isTrue(config.contains("z")); + Validate.isTrue(config.contains("distance")); + Validate.isTrue(Bukkit.getWorld(config.getString("world"))!=null,"This world doesn't exist"); + location=new Location(Bukkit.getWorld(config.getString("world")),config.getDouble("x"), + config.getDouble("y"),config.getDouble("z")); + distance=config.getDouble("distance"); + } + + @Override + public boolean isMet(ConditionInstance entity) { + Entity entity1=entity.getEntity(); + return entity1.getWorld().equals(location.getWorld())&&location.distance(entity1.getLocation()) dynamicUseConditions = new ArrayList<>(); + private final Map costs = new HashMap<>(); public double getDynamicCost() { @@ -55,21 +62,39 @@ public class Waypoint implements Unlockable { loc = readLocation(Objects.requireNonNull(config.getString("location"), "Could not read location")); radiusSquared = Math.pow(config.getDouble("radius"), 2); - dynamicCost=config.getDouble("cost.dynamic-use"); - setSpawnCost=config.getDouble("cost.set-spawnpoint"); + dynamicCost = config.getDouble("cost.dynamic-use"); + setSpawnCost = config.getDouble("cost.set-spawnpoint"); for (WaypointOption option : WaypointOption.values()) options.put(option, config.getBoolean("option." + option.getPath(), option.getDefaultValue())); //We load all the linked WayPoints - ConfigurationSection section=config.getConfigurationSection("linked"); - for(String key: section.getKeys(false)) { - destinations.put(key,config.getInt(key)); + if (config.contains("linked")) { + ConfigurationSection section = config.getConfigurationSection("linked"); + for (String key : section.getKeys(false)) { + destinations.put(key, section.getInt(key)); + } } + if (config.contains("conditions")) { + List conditions = config.getStringList("conditions"); + for (String condition : conditions) { + dynamicUseConditions.add(MMOCore.plugin.loadManager.loadCondition(new MMOLineConfig(condition))); + } - //destinations.addAll(config.getStringList("linked")); + } + } + + public boolean canHaveDynamicUse(Player player) { + if (!options.get(WaypointOption.DYNAMIC)) + return false; + + for (Condition condition : dynamicUseConditions) { + if (!condition.isMet(new ConditionInstance(player))) + return false; + } + return true; } public String getId() { @@ -149,7 +174,7 @@ public class Waypoint implements Unlockable { paths.add(checked); checkedPoints.add(checked.getFinalWaypoint()); - if(checked.getFinalWaypoint().equals(targetWaypoint)) + if (checked.getFinalWaypoint().equals(targetWaypoint)) return checked; for (String wayPointId : checked.getFinalWaypoint().destinations.keySet()) { @@ -213,7 +238,7 @@ public class Waypoint implements Unlockable { return new Location(world, x, y, z, yaw, pitch); } - public class PathInfo { + public static class PathInfo { private final ArrayList waypoints; private final double cost; @@ -226,10 +251,15 @@ public class Waypoint implements Unlockable { } public PathInfo(Waypoint waypoint) { - this.waypoints = (ArrayList) Arrays.asList(waypoint); + this.waypoints = new ArrayList<>(); + this.waypoints.add(waypoint); cost = 0; } - + public PathInfo(Waypoint waypoint,double cost) { + this.waypoints = new ArrayList<>(); + this.waypoints.add(waypoint); + this.cost = cost; + } public ArrayList addInOrder(ArrayList pathInfos) { int index = 0; @@ -238,6 +268,7 @@ public class Waypoint implements Unlockable { pathInfos.set(index, this); return pathInfos; } + index++; } //If index==pathInfos.size() we add the waypoint at the end pathInfos.add(this); @@ -245,29 +276,43 @@ public class Waypoint implements Unlockable { } - - public PathInfo(ArrayList waypoints, double cost) { - this.waypoints = waypoints; + public PathInfo(List waypoints, double cost) { + this.waypoints = new ArrayList<>(waypoints); this.cost = cost; } - public String displayIntermediaryWayPoints() { - if(waypoints.size()<=2) - return ""; - String result=""; - for(int i=1;i newWaypoints = new ArrayList<>(); newWaypoints.addAll(waypoints); newWaypoints.add(waypoint); - double cost=this.cost+getFinalWaypoint().getSimpleCostDestination(waypoint); - return new PathInfo(newWaypoints,cost); + double cost = this.cost + getFinalWaypoint().getSimpleCostDestination(waypoint); + return new PathInfo(newWaypoints, cost); } diff --git a/src/main/resources/default/gui/waypoints.yml b/src/main/resources/default/gui/waypoints.yml index aa696e33..ff58f2c2 100644 --- a/src/main/resources/default/gui/waypoints.yml +++ b/src/main/resources/default/gui/waypoints.yml @@ -45,6 +45,13 @@ items: - '&7You cannot teleport as you are not standing on a waypoint.' + current-waypoint: + name: '&a{name}' + item: ENDER_PEARL + + lore: + - '&7The waypoint you are standing at.' + # When you don't have enough stellium not-enough-stellium: name: '&a{name}' @@ -61,7 +68,7 @@ items: lore: - '&7You can teleport to this waypoint.' - - '&7 Intermediary waypoints : {intermediary_waypoints}' + - '&7Intermediary waypoints : {intermediary_waypoints}' - '&7Click to teleport for &b{current_cost} &7Stellium.' next: diff --git a/src/main/resources/default/professions/fishing.yml b/src/main/resources/default/professions/fishing.yml index c26e6a6a..ed2b9985 100644 --- a/src/main/resources/default/professions/fishing.yml +++ b/src/main/resources/default/professions/fishing.yml @@ -22,7 +22,7 @@ exp-sources: {} on-fish: overriding-drop-table: conditions: - - 'region{name=swamp}' + - 'biome{name=swamp}' # When drop table is read, one of these # items will be selected randomly. diff --git a/src/main/resources/default/stats.yml b/src/main/resources/default/stats.yml index 1c9ef218..21fdee82 100644 --- a/src/main/resources/default/stats.yml +++ b/src/main/resources/default/stats.yml @@ -58,6 +58,11 @@ default: COOLDOWN_REDUCTION: base: 0 per-level: 0 + + #Increases chance to have rare loot chests + CHANCE: + base: 0 + per-level: 0 # Dealt by skills SKILL_DAMAGE: diff --git a/src/main/resources/default/waypoints.yml b/src/main/resources/default/waypoints.yml index 59999359..1de64a36 100644 --- a/src/main/resources/default/waypoints.yml +++ b/src/main/resources/default/waypoints.yml @@ -17,8 +17,6 @@ spawn: # Stellium cost in order to use the waypoint. # Stellium is like stamina however it's not used # by skills and regens much slower than mana. - normal-use: 3 - # Cost when not standing on any waypoint. dynamic-use: 5 @@ -42,12 +40,12 @@ spawn: default: true # All the waypoints you can teleport to when standing - # on that waypoint. If that list is empty you are able + # on that waypoint. And the cost needed to travel to his pointIf that list is empty you are able # to teleport to any waypoint linked: - - spawn1 - - spawn2 - - forest + spawn1: 4 + spawn2: 5 + spawn1: name: Spawn1 @@ -62,6 +60,15 @@ spawn1: # on any waypoint (waypoint must be unlocked). dynamic: true + ##Not necessary if the waypoint doesn't allow dynamic use + ##The conditions for the dynamic-use of the waypoint + conditions: + - 'distance{world=world;x=69;y=71;z=163;distance=500}' + + + linked: + spawn: 4 + spawn2: name: Spawn2 location: 'world 69 71 136 136 0' @@ -70,3 +77,5 @@ spawn2: normal-use: 3 option: enable-menu: false + linked: + spawn: 3