From d8af4ba8bc31b305b421633697720e78835ea804 Mon Sep 17 00:00:00 2001 From: Indyuce Date: Tue, 14 Apr 2020 01:07:57 +0200 Subject: [PATCH] !API needed for loot chests --- lib/MMOLib.jar | Bin 165315 -> 168714 bytes .../java/net/Indyuce/mmocore/MMOCore.java | 25 ++- .../Indyuce/mmocore/api/block/BlockInfo.java | 5 +- .../mmocore/api/droptable/DropTable.java | 17 +- .../api/droptable/dropitem/DropItem.java | 15 +- .../droptable/dropitem/DropTableDropItem.java | 10 +- .../api/droptable/dropitem/GoldDropItem.java | 9 +- .../dropitem/MMDropTableDropItem.java | 26 ++- .../api/droptable/dropitem/NoteDropItem.java | 11 +- .../droptable/dropitem/VanillaDropItem.java | 9 +- .../dropitem/fishing/FishingDropItem.java | 10 +- .../api/event/CustomBlockMineEvent.java | 3 +- .../api/event/LootChestSpawnEvent.java | 51 ++++++ .../api/loot/ChestAlgorithmOptions.java | 50 ++++++ .../Indyuce/mmocore/api/loot/ChestTier.java | 47 ++--- .../Indyuce/mmocore/api/loot/LootBuilder.java | 48 +++++ .../Indyuce/mmocore/api/loot/LootChest.java | 70 +++++++- .../mmocore/api/loot/LootChestRegion.java | 130 +++++++++++++- .../mmocore/api/loot/LootChestRunnable.java | 28 +++ .../mmocore/api/loot/RegionBounds.java | 36 ++-- .../Indyuce/mmocore/api/loot/TierEffect.java | 37 ++++ .../mmocore/api/player/social/Party.java | 24 ++- .../mmocore/api/player/stats/PlayerStats.java | 4 +- .../mmocore/api/player/stats/StatType.java | 6 + .../api/skill/result/LocationSkillResult.java | 2 +- .../api/skill/result/TargetSkillResult.java | 2 +- .../api/util/math/formula/RandomAmount.java | 5 +- .../listener/LootableChestsListener.java | 8 +- .../mmocore/manager/ConfigManager.java | 5 +- .../mmocore/manager/DropTableManager.java | 9 +- .../mmocore/manager/LootChestManager.java | 75 ++++++++ .../mmocore/manager/LootableChestManager.java | 169 ------------------ src/main/resources/config.yml | 8 + .../resources/default/professions/mining.yml | 2 - 34 files changed, 629 insertions(+), 327 deletions(-) create mode 100644 src/main/java/net/Indyuce/mmocore/api/event/LootChestSpawnEvent.java create mode 100644 src/main/java/net/Indyuce/mmocore/api/loot/ChestAlgorithmOptions.java create mode 100644 src/main/java/net/Indyuce/mmocore/api/loot/LootBuilder.java create mode 100644 src/main/java/net/Indyuce/mmocore/api/loot/LootChestRunnable.java create mode 100644 src/main/java/net/Indyuce/mmocore/api/loot/TierEffect.java create mode 100644 src/main/java/net/Indyuce/mmocore/manager/LootChestManager.java delete mode 100644 src/main/java/net/Indyuce/mmocore/manager/LootableChestManager.java diff --git a/lib/MMOLib.jar b/lib/MMOLib.jar index ce864bd55259c966487b61e72d52ddd732f6089c..6a94bb01892e1ab5515c75474a0e4cd2277a87e4 100644 GIT binary patch delta 10643 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.getActive())) + if (chest.shouldExpire()) + chest.unregister(false); + + } + }.runTaskTimer(this, 5 * 60 * 20, 5 * 60 * 20); + /* * For the sake of the lord, make sure they aren't using MMOItems Mana * and Stamina Addon...This should prevent a couple error reports @@ -360,6 +376,8 @@ public class MMOCore extends JavaPlugin { dataProvider.getGuildManager().save(guild); mineManager.resetRemainingBlocks(); + + lootChests.getActive().forEach(chest -> chest.unregister(false)); } public void reloadPlugin() { @@ -396,7 +414,8 @@ public class MMOCore extends JavaPlugin { questManager.clear(); questManager.reload(); - chestManager = new LootableChestManager(new ConfigFile("chests").getConfig()); + lootChests.reload(); + waypointManager = new WaypointManager(new ConfigFile("waypoints").getConfig()); restrictionManager = new RestrictionManager(new ConfigFile("restrictions").getConfig()); requestManager = new RequestManager(); diff --git a/src/main/java/net/Indyuce/mmocore/api/block/BlockInfo.java b/src/main/java/net/Indyuce/mmocore/api/block/BlockInfo.java index 16c64292..ae94a12a 100644 --- a/src/main/java/net/Indyuce/mmocore/api/block/BlockInfo.java +++ b/src/main/java/net/Indyuce/mmocore/api/block/BlockInfo.java @@ -13,6 +13,7 @@ import org.bukkit.inventory.ItemStack; import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.droptable.DropTable; import net.Indyuce.mmocore.api.load.MMOLoadException; +import net.Indyuce.mmocore.api.loot.LootBuilder; import net.Indyuce.mmocore.api.quest.trigger.ExperienceTrigger; import net.Indyuce.mmocore.api.quest.trigger.Trigger; import net.mmogroup.mmolib.api.MMOLineConfig; @@ -76,8 +77,8 @@ public class BlockInfo { return table; } - public List collectDrops() { - return hasDropTable() ? table.collect() : new ArrayList<>(); + public List collectDrops(LootBuilder builder) { + return hasDropTable() ? table.collect(builder) : new ArrayList<>(); } public boolean hasDropTable() { diff --git a/src/main/java/net/Indyuce/mmocore/api/droptable/DropTable.java b/src/main/java/net/Indyuce/mmocore/api/droptable/DropTable.java index 36329a7a..f54719d5 100644 --- a/src/main/java/net/Indyuce/mmocore/api/droptable/DropTable.java +++ b/src/main/java/net/Indyuce/mmocore/api/droptable/DropTable.java @@ -1,7 +1,6 @@ package net.Indyuce.mmocore.api.droptable; -import java.util.ArrayList; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -12,11 +11,12 @@ import org.bukkit.inventory.ItemStack; import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.droptable.dropitem.DropItem; import net.Indyuce.mmocore.api.load.MMOLoadException; +import net.Indyuce.mmocore.api.loot.LootBuilder; import net.mmogroup.mmolib.api.MMOLineConfig; public class DropTable { private final String id; - private final Set drops = new HashSet<>(); + private final Set drops = new LinkedHashSet<>(); /* * cached in order to load other items. @@ -52,13 +52,14 @@ public class DropTable { return id; } - public List collect() { - List total = new ArrayList<>(); + public List collect(LootBuilder builder) { for (DropItem item : drops) - if (item.rollChance()) - item.collect(total); + if (item.rollChance() && builder.getCapacity() >= item.getWeight()) { + item.collect(builder); + builder.reduceCapacity(item.getWeight()); + } - return total; + return builder.getLoot(); } } \ No newline at end of file diff --git a/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/DropItem.java b/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/DropItem.java index 94ceae76..2edda920 100644 --- a/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/DropItem.java +++ b/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/DropItem.java @@ -1,22 +1,21 @@ package net.Indyuce.mmocore.api.droptable.dropitem; -import java.util.List; import java.util.Random; -import org.bukkit.inventory.ItemStack; - +import net.Indyuce.mmocore.api.loot.LootBuilder; import net.Indyuce.mmocore.api.util.math.formula.RandomAmount; import net.mmogroup.mmolib.api.MMOLineConfig; public abstract class DropItem { protected static final Random random = new Random(); - private double chance; - private RandomAmount amount; + private final double chance, weight; + private final RandomAmount amount; public DropItem(MMOLineConfig config) { chance = config.args().length > 0 ? Double.parseDouble(config.args()[0]) : 1; amount = config.args().length > 1 ? new RandomAmount(config.args()[1]) : new RandomAmount(1, 0); + weight = config.args().length > 2 ? Double.parseDouble(config.args()[2]) : 0; } public RandomAmount getAmount() { @@ -27,6 +26,10 @@ public abstract class DropItem { return chance; } + public double getWeight() { + return weight; + } + public int rollAmount() { return amount.calculateInt(); } @@ -35,5 +38,5 @@ public abstract class DropItem { return random.nextDouble() < chance; } - public abstract void collect(List total); + public abstract void collect(LootBuilder builder); } diff --git a/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/DropTableDropItem.java b/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/DropTableDropItem.java index 975c1caa..d664ba0d 100644 --- a/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/DropTableDropItem.java +++ b/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/DropTableDropItem.java @@ -1,16 +1,14 @@ package net.Indyuce.mmocore.api.droptable.dropitem; -import java.util.List; - import org.apache.commons.lang.Validate; -import org.bukkit.inventory.ItemStack; import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.droptable.DropTable; +import net.Indyuce.mmocore.api.loot.LootBuilder; import net.mmogroup.mmolib.api.MMOLineConfig; public class DropTableDropItem extends DropItem { - private DropTable dropTable; + private final DropTable dropTable; public DropTableDropItem(MMOLineConfig config) { super(config); @@ -23,8 +21,8 @@ public class DropTableDropItem extends DropItem { } @Override - public void collect(List total) { + public void collect(LootBuilder builder) { for (int j = 0; j < rollAmount(); j++) - total.addAll(dropTable.collect()); + builder.addLoot(dropTable.collect(builder)); } } diff --git a/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/GoldDropItem.java b/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/GoldDropItem.java index 3e621a34..db70e947 100644 --- a/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/GoldDropItem.java +++ b/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/GoldDropItem.java @@ -1,9 +1,6 @@ package net.Indyuce.mmocore.api.droptable.dropitem; -import java.util.List; - -import org.bukkit.inventory.ItemStack; - +import net.Indyuce.mmocore.api.loot.LootBuilder; import net.Indyuce.mmocore.api.util.item.CurrencyItem; import net.mmogroup.mmolib.api.MMOLineConfig; @@ -13,7 +10,7 @@ public class GoldDropItem extends DropItem { } @Override - public void collect(List total) { - total.add(new CurrencyItem("GOLD_COIN", 1, rollAmount()).build()); + public void collect(LootBuilder builder) { + builder.addLoot(new CurrencyItem("GOLD_COIN", 1, rollAmount()).build()); } } diff --git a/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/MMDropTableDropItem.java b/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/MMDropTableDropItem.java index d001626e..08c95270 100644 --- a/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/MMDropTableDropItem.java +++ b/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/MMDropTableDropItem.java @@ -1,10 +1,6 @@ package net.Indyuce.mmocore.api.droptable.dropitem; -import java.util.List; import java.util.NoSuchElementException; -import java.util.logging.Level; - -import org.bukkit.inventory.ItemStack; import io.lumine.xikage.mythicmobs.MythicMobs; import io.lumine.xikage.mythicmobs.adapters.bukkit.BukkitAdapter; @@ -13,12 +9,13 @@ import io.lumine.xikage.mythicmobs.drops.DropMetadata; import io.lumine.xikage.mythicmobs.drops.DropTable; import io.lumine.xikage.mythicmobs.drops.IItemDrop; import io.lumine.xikage.mythicmobs.drops.LootBag; -import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.loot.LootBuilder; import net.mmogroup.mmolib.api.MMOLineConfig; public class MMDropTableDropItem extends DropItem { - private DropTable dropTable; - private DropMetadata metadata = new DropMetadata(null, null); + private final DropTable dropTable; + + private static final DropMetadata metadata = new DropMetadata(null, null); public MMDropTableDropItem(MMOLineConfig config) { super(config); @@ -28,19 +25,16 @@ public class MMDropTableDropItem extends DropItem { try { dropTable = MythicMobs.inst().getDropManager().getDropTable(id).get(); - } catch(NoSuchElementException e) { - MMOCore.log(Level.WARNING, "Could not find MM drop table" + id); + } catch (NoSuchElementException exception) { + throw new IllegalArgumentException("Could not find MM drop table with ID '" + id + "'"); } } @Override - public void collect(List total) { + public void collect(LootBuilder builder) { LootBag lootBag = dropTable.generate(metadata); - - for(Drop type : lootBag.getDrops()) { - if(type instanceof IItemDrop) { - total.add(BukkitAdapter.adapt(((IItemDrop)type).getDrop(metadata))); - } - } + for (Drop type : lootBag.getDrops()) + if (type instanceof IItemDrop) + builder.addLoot(BukkitAdapter.adapt(((IItemDrop) type).getDrop(metadata))); } } diff --git a/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/NoteDropItem.java b/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/NoteDropItem.java index 77a69e9d..c37f5025 100644 --- a/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/NoteDropItem.java +++ b/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/NoteDropItem.java @@ -1,14 +1,11 @@ package net.Indyuce.mmocore.api.droptable.dropitem; -import java.util.List; - -import org.bukkit.inventory.ItemStack; - +import net.Indyuce.mmocore.api.loot.LootBuilder; import net.Indyuce.mmocore.api.util.item.CurrencyItem; import net.mmogroup.mmolib.api.MMOLineConfig; public class NoteDropItem extends DropItem { - private int min, max; + private final int min, max; public NoteDropItem(MMOLineConfig config) { super(config); @@ -20,7 +17,7 @@ public class NoteDropItem extends DropItem { } @Override - public void collect(List total) { - total.add(new CurrencyItem("NOTE", random.nextInt(max - min + 1) + min, rollAmount()).build()); + public void collect(LootBuilder builder) { + builder.addLoot(new CurrencyItem("NOTE", random.nextInt(max - min + 1) + min, rollAmount()).build()); } } diff --git a/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/VanillaDropItem.java b/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/VanillaDropItem.java index 4ebd1c81..cecf92d1 100644 --- a/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/VanillaDropItem.java +++ b/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/VanillaDropItem.java @@ -1,10 +1,9 @@ package net.Indyuce.mmocore.api.droptable.dropitem; -import java.util.List; - import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import net.Indyuce.mmocore.api.loot.LootBuilder; import net.mmogroup.mmolib.api.MMOLineConfig; public class VanillaDropItem extends DropItem { @@ -16,13 +15,13 @@ public class VanillaDropItem extends DropItem { config.validate("type"); this.material = Material.valueOf(config.getString("type")); } - + public Material getMaterial() { return material; } @Override - public void collect(List total) { - total.add(new ItemStack(material, rollAmount())); + public void collect(LootBuilder builder) { + builder.addLoot(new ItemStack(material, rollAmount())); } } diff --git a/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/fishing/FishingDropItem.java b/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/fishing/FishingDropItem.java index 1ac7359f..55adfad2 100644 --- a/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/fishing/FishingDropItem.java +++ b/src/main/java/net/Indyuce/mmocore/api/droptable/dropitem/fishing/FishingDropItem.java @@ -1,12 +1,10 @@ package net.Indyuce.mmocore.api.droptable.dropitem.fishing; -import java.util.ArrayList; -import java.util.List; - import org.bukkit.inventory.ItemStack; import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.droptable.dropitem.DropItem; +import net.Indyuce.mmocore.api.loot.LootBuilder; import net.Indyuce.mmocore.api.util.math.formula.RandomAmount; import net.mmogroup.mmolib.api.MMOLineConfig; @@ -58,8 +56,8 @@ public class FishingDropItem { } public ItemStack collect() { - List collect = new ArrayList<>(); - dropItem.collect(collect); - return collect.stream().findAny().get(); + LootBuilder builder = new LootBuilder(null, 0); + dropItem.collect(builder); + return builder.getLoot().stream().findAny().get(); } } diff --git a/src/main/java/net/Indyuce/mmocore/api/event/CustomBlockMineEvent.java b/src/main/java/net/Indyuce/mmocore/api/event/CustomBlockMineEvent.java index 5ee1cf5f..a72228d6 100644 --- a/src/main/java/net/Indyuce/mmocore/api/event/CustomBlockMineEvent.java +++ b/src/main/java/net/Indyuce/mmocore/api/event/CustomBlockMineEvent.java @@ -9,6 +9,7 @@ import org.bukkit.inventory.ItemStack; import net.Indyuce.mmocore.api.block.BlockInfo; import net.Indyuce.mmocore.api.experience.ExperienceInfo; +import net.Indyuce.mmocore.api.loot.LootBuilder; import net.Indyuce.mmocore.api.player.PlayerData; public class CustomBlockMineEvent extends PlayerDataEvent implements Cancellable { @@ -24,7 +25,7 @@ public class CustomBlockMineEvent extends PlayerDataEvent implements Cancellable super(player); this.block = block; - this.drops = info.collectDrops(); + this.drops = info.collectDrops(new LootBuilder(player, 0)); this.experience = info.hasExperience() ? info.getExperience().newInfo() : null; } diff --git a/src/main/java/net/Indyuce/mmocore/api/event/LootChestSpawnEvent.java b/src/main/java/net/Indyuce/mmocore/api/event/LootChestSpawnEvent.java new file mode 100644 index 00000000..df5e75c2 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/api/event/LootChestSpawnEvent.java @@ -0,0 +1,51 @@ +package net.Indyuce.mmocore.api.event; + +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +import net.Indyuce.mmocore.api.loot.LootBuilder; +import net.Indyuce.mmocore.api.loot.LootChest; +import net.Indyuce.mmocore.api.player.PlayerData; + +public class LootChestSpawnEvent extends PlayerDataEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + + private final LootChest chest; + private final LootBuilder loot; + + private boolean cancelled; + + public LootChestSpawnEvent(PlayerData playerData, LootChest chest, LootBuilder loot) { + super(playerData); + + this.chest = chest; + this.loot = loot; + } + + public LootChest getChest() { + return chest; + } + + public LootBuilder getLoot() { + return loot; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/main/java/net/Indyuce/mmocore/api/loot/ChestAlgorithmOptions.java b/src/main/java/net/Indyuce/mmocore/api/loot/ChestAlgorithmOptions.java new file mode 100644 index 00000000..bd305605 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/api/loot/ChestAlgorithmOptions.java @@ -0,0 +1,50 @@ +package net.Indyuce.mmocore.api.loot; + +import org.apache.commons.lang.Validate; +import org.bukkit.configuration.ConfigurationSection; + +public class ChestAlgorithmOptions { + + /* + * min and max range represents the range at which the chest can spawn + * around the player. height is the Z delta in which the chest can spawn, + * relative to the player's altitude + */ + public final double minRange, maxRange, height; + + /* + * maximum amount of trials the algorithm will run in order to find a + * suitable location for a chest around the player. + */ + public final int iterations; + + public static final ChestAlgorithmOptions DEFAULT = new ChestAlgorithmOptions(10, 30, 8, 15); + + /* + * this is purely to let server owners tweak the chest random location + * finder algorithm. + */ + public ChestAlgorithmOptions(ConfigurationSection config) { + Validate.notNull(config, "Config cannot be nulm"); + + minRange = config.getDouble("min-range", DEFAULT.minRange); + maxRange = config.getDouble("max-range", DEFAULT.maxRange); + height = config.getDouble("height", DEFAULT.height); + iterations = config.getInt("iterations", DEFAULT.iterations); + + Validate.isTrue(minRange < maxRange, "Max range must be greater than min range"); + Validate.isTrue(height > 0, "Height must be strictly positive"); + Validate.isTrue(iterations > 0, "Iterations must be strictly positive"); + } + + /* + * can be used to register loot chest regions with external plugins, and + * used by the default alg options instance + */ + public ChestAlgorithmOptions(double minRange, double maxRange, double height, int iterations) { + this.minRange = minRange; + this.maxRange = maxRange; + this.height = height; + this.iterations = iterations; + } +} diff --git a/src/main/java/net/Indyuce/mmocore/api/loot/ChestTier.java b/src/main/java/net/Indyuce/mmocore/api/loot/ChestTier.java index 364be184..b547177a 100644 --- a/src/main/java/net/Indyuce/mmocore/api/loot/ChestTier.java +++ b/src/main/java/net/Indyuce/mmocore/api/loot/ChestTier.java @@ -1,24 +1,32 @@ package net.Indyuce.mmocore.api.loot; -import org.apache.commons.lang.Validate; -import org.bukkit.Location; -import org.bukkit.Particle; import org.bukkit.configuration.ConfigurationSection; -import net.Indyuce.mmocore.api.util.math.particle.ChestParticleEffect; +import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.droptable.DropTable; +import net.Indyuce.mmocore.api.player.PlayerData; +import net.mmogroup.mmolib.api.math.ScalingFormula; public class ChestTier { private final TierEffect effect; - private final int weight; + private final ScalingFormula capacity; + private final DropTable table; + + public final double chance; public ChestTier(ConfigurationSection config) { - effect = config.isConfigurationSection("effect") ? new TierEffect(config.getConfigurationSection("effect")) - : null; - weight = config.getInt("weight", 1); + effect = config.isConfigurationSection("effect") ? new TierEffect(config.getConfigurationSection("effect")) : null; + capacity = new ScalingFormula(config.get("capacity")); + chance = config.getDouble("chance"); + table = MMOCore.plugin.dropTableManager.loadDropTable(config.get("drops")); } - - public int getWeight() { - return weight; + + public double rollCapacity(PlayerData player) { + return capacity.calculate(player.getLevel()); + } + + public DropTable getDropTable() { + return table; } public boolean hasEffect() { @@ -28,21 +36,4 @@ public class ChestTier { public TierEffect getEffect() { return effect; } - - public class TierEffect { - private final ChestParticleEffect type; - private final Particle particle; - - public TierEffect(ConfigurationSection config) { - Validate.notNull(config, "Could not load tier config"); - type = ChestParticleEffect - .valueOf(config.getString("type", "OFFSET").toUpperCase().replace("-", "_").replace(" ", "_")); - particle = Particle - .valueOf(config.getString("particle", "FLAME").toUpperCase().replace("-", "_").replace(" ", "_")); - } - - public void play(Location loc) { - type.play(loc, particle); - } - } } diff --git a/src/main/java/net/Indyuce/mmocore/api/loot/LootBuilder.java b/src/main/java/net/Indyuce/mmocore/api/loot/LootBuilder.java new file mode 100644 index 00000000..873573f7 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/api/loot/LootBuilder.java @@ -0,0 +1,48 @@ +package net.Indyuce.mmocore.api.loot; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.inventory.ItemStack; + +import net.Indyuce.mmocore.api.player.PlayerData; + +public class LootBuilder { + private final PlayerData player; + private final List loot = new ArrayList<>(); + + private double capacity; + + /* + * instance which saves what entity is currently rolling a loot table and + * how much item capacity the table has left + */ + public LootBuilder(PlayerData player, double capacity) { + this.player = player; + this.capacity = capacity; + } + + public PlayerData getEntity() { + return player; + } + + public List getLoot() { + return loot; + } + + public double getCapacity() { + return capacity; + } + + public void addLoot(ItemStack item) { + loot.add(item); + } + + public void addLoot(List items) { + loot.addAll(items); + } + + public void reduceCapacity(double value) { + this.capacity = Math.max(0, capacity - value); + } +} diff --git a/src/main/java/net/Indyuce/mmocore/api/loot/LootChest.java b/src/main/java/net/Indyuce/mmocore/api/loot/LootChest.java index 41ba5119..7d4aff55 100644 --- a/src/main/java/net/Indyuce/mmocore/api/loot/LootChest.java +++ b/src/main/java/net/Indyuce/mmocore/api/loot/LootChest.java @@ -2,20 +2,37 @@ package net.Indyuce.mmocore.api.loot; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; import org.bukkit.block.Block; +import org.bukkit.block.Chest; import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import net.Indyuce.mmocore.MMOCore; public class LootChest { + private final ChestTier tier; private final LootChestRegion region; + private final ReplacedBlock block; + private final BukkitRunnable effectRunnable; + private final long date = System.currentTimeMillis(); /* - * saves data of block replaced + * instance generated when a loot chest is placed (as a bukkit block), and + * used to save the data of the block which has been replaced (can replace + * non-solid blocks) */ - private final ReplacedBlock block; - - public LootChest(LootChestRegion region, Block block) { + public LootChest(ChestTier tier, LootChestRegion region, Block block) { + this.tier = tier; this.region = region; this.block = new ReplacedBlock(block); + this.effectRunnable = tier.hasEffect() ? tier.getEffect().startNewRunnable(block.getLocation().add(.5, .5, .5)) : null; + } + + public ChestTier getTier() { + return tier; } public ReplacedBlock getBlock() { @@ -26,6 +43,40 @@ public class LootChest { return region; } + public boolean hasPlayerNearby() { + for (Player player : block.loc.getWorld().getPlayers()) + if (player.getLocation().distanceSquared(block.loc) < 625) + return true; + return false; + } + + public boolean shouldExpire() { + return System.currentTimeMillis() - date > MMOCore.plugin.configManager.lootChestExpireTime; + } + + public void unregister(boolean player) { + + /* + * if a player is responsible of closing the chest, close it with sound + */ + if (player) { + block.loc.getWorld().playSound(block.loc, Sound.ITEM_ARMOR_EQUIP_LEATHER, 1, 1); + block.loc.getWorld().spawnParticle(Particle.CRIT, block.loc.clone().add(.5, .5, .5), 16, 0, 0, 0, .5); + MMOCore.plugin.lootChests.unregister(this); + } + + /* + * must clean block inventory before replacing block otherwise loots fly + * off and accumulate on the ground (+during dev phase) + */ + else + ((Chest) block.loc.getBlock().getState()).getBlockInventory().clear(); + + block.restore(); + if (effectRunnable != null) + effectRunnable.cancel(); + } + public class ReplacedBlock { private final Material material; private final BlockData data; @@ -34,7 +85,16 @@ public class LootChest { public ReplacedBlock(Block block) { this.material = block.getType(); this.data = block.getBlockData(); - loc = block.getLocation(); + this.loc = block.getLocation(); + } + + public Location getLocoation() { + return loc; + } + + public boolean matches(Location loc) { + return this.loc.getWorld().equals(loc.getWorld()) && this.loc.getBlockX() == loc.getBlockX() && this.loc.getBlockY() == loc.getBlockY() + && this.loc.getBlockZ() == loc.getBlockZ(); } public void restore() { diff --git a/src/main/java/net/Indyuce/mmocore/api/loot/LootChestRegion.java b/src/main/java/net/Indyuce/mmocore/api/loot/LootChestRegion.java index 5d078160..586cffe3 100644 --- a/src/main/java/net/Indyuce/mmocore/api/loot/LootChestRegion.java +++ b/src/main/java/net/Indyuce/mmocore/api/loot/LootChestRegion.java @@ -1,31 +1,44 @@ package net.Indyuce.mmocore.api.loot; -import java.util.HashSet; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Random; import java.util.Set; import java.util.logging.Level; import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Chest; import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.util.Vector; import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.event.LootChestSpawnEvent; +import net.Indyuce.mmocore.api.player.PlayerData; public class LootChestRegion { private final String id; private final long chestSpawnPeriod; private final RegionBounds bounds; - private final Set tiers = new HashSet<>(); - - /* - * last time - */ + private final ChestAlgorithmOptions algOptions; + private final Set tiers = new LinkedHashSet<>(); + private final BukkitRunnable runnable; + + private static final Random random = new Random(); public LootChestRegion(ConfigurationSection config) { Validate.notNull(config, "Could not load config"); - id = config.getName(); + id = config.getName().toLowerCase().replace("_", "-").replace(" ", "-"); bounds = new RegionBounds(config.getConfigurationSection("bounds")); - chestSpawnPeriod = config.getInt("spawn-period"); + chestSpawnPeriod = config.getLong("spawn-period", 5 * 60); + algOptions = config.contains("algorithm-options") ? new ChestAlgorithmOptions(config.getConfigurationSection("algorithm-options")) + : ChestAlgorithmOptions.DEFAULT; Validate.isTrue(config.isConfigurationSection("tiers"), "Could not find chest tiers"); for (String key : config.getConfigurationSection("tiers").getKeys(false)) @@ -35,6 +48,10 @@ public class LootChestRegion { MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load tier '" + key + "' from chest region '" + id + "': " + exception.getMessage()); } + + Validate.isTrue(!tiers.isEmpty(), "Your region must have at least one chest tier"); + + runnable = new LootChestRunnable(this); } public String getId() { @@ -52,4 +69,101 @@ public class LootChestRegion { public long getChestSpawnPeriod() { return chestSpawnPeriod; } + + public BukkitRunnable getRunnable() { + return runnable; + } + + public void spawnChest(PlayerData player) { + + // first randomly determine the chest tier + ChestTier tier = rollTier(); + + // find a random location, 20 trials max + Location location = getRandomLocation(player.getPlayer().getLocation()); + if (location == null) + return; + + LootChest lootChest = new LootChest(tier, this, location.getBlock()); + LootBuilder builder = new LootBuilder(player, tier.rollCapacity(player)); + tier.getDropTable().collect(builder); + + LootChestSpawnEvent event = new LootChestSpawnEvent(player, lootChest, builder); + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) + return; + + List slots = new ArrayList<>(); + for (int j = 0; j < 27; j++) + slots.add(j); + + location.getBlock().setType(Material.CHEST); + Chest chest = (Chest) location.getBlock().getState(); + tier.getDropTable().collect(builder).forEach(item -> { + Integer slot = slots.get(random.nextInt(slots.size())); + chest.getInventory().setItem(slot, item); + slots.remove(slot); + }); + + MMOCore.plugin.lootChests.register(lootChest); + } + + // TODO improve + public ChestTier rollTier() { + + double s = 0; + for (ChestTier tier : tiers) { + if (random.nextDouble() < tier.chance / (1 - s)) + return tier; + s += tier.chance; + + } + + return tiers.stream().findAny().get(); + } + + public Location getRandomLocation(Location center) { + + for (int j = 0; j < algOptions.iterations; j++) { + Location random = tryRandomDirection(center); + if (random != null) + return random; + } + + /* + * no location has been found after the X iterations, return null and + * cancel chest spawning. worst case scenario, should not happen too + * often except if the player is in a really NARROW zone + */ + return null; + } + + public Location tryRandomDirection(Location center) { + + /* + * chooses a random direction and get the block in that direction which + * has the same height as the player + */ + double a = random.nextDouble() * 2 * Math.PI; + Vector dir = new Vector(Math.cos(a), 0, Math.sin(a)) + .multiply(algOptions.minRange + random.nextDouble() * (algOptions.maxRange - algOptions.minRange)); + Location random = center.add(dir); + + /* + * go up and down at the same time till it finds a non-solid block with + * a solid block underneath + */ + for (int h = 0; h <= algOptions.height * 2; h++) { + int z = h % 2 == 0 ? h / 2 : -(h + 1) / 2; // bijective from N to Z + Location checked = random.clone().add(0, z, 0); + if (isSuitable(checked)) + return checked; + } + + return null; + } + + private boolean isSuitable(Location loc) { + return !loc.getBlock().getType().isSolid() && loc.clone().add(0, -1, 0).getBlock().getType().isSolid(); + } } diff --git a/src/main/java/net/Indyuce/mmocore/api/loot/LootChestRunnable.java b/src/main/java/net/Indyuce/mmocore/api/loot/LootChestRunnable.java new file mode 100644 index 00000000..7e341a08 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/api/loot/LootChestRunnable.java @@ -0,0 +1,28 @@ +package net.Indyuce.mmocore.api.loot; + +import java.util.Optional; + +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.player.PlayerData; + +public class LootChestRunnable extends BukkitRunnable { + private final LootChestRegion region; + + public LootChestRunnable(LootChestRegion region) { + this.region = region; + + runTaskTimer(MMOCore.plugin, region.getChestSpawnPeriod() * 20, region.getChestSpawnPeriod() * 20); + } + + // TODO add option so that players cannot have more than X chests every X + // time + @Override + public void run() { + Optional found = region.getBounds().getPlayers().findAny(); + if (found.isPresent()) + region.spawnChest(PlayerData.get(found.get())); + } +} diff --git a/src/main/java/net/Indyuce/mmocore/api/loot/RegionBounds.java b/src/main/java/net/Indyuce/mmocore/api/loot/RegionBounds.java index a59871de..c162aa37 100644 --- a/src/main/java/net/Indyuce/mmocore/api/loot/RegionBounds.java +++ b/src/main/java/net/Indyuce/mmocore/api/loot/RegionBounds.java @@ -1,7 +1,6 @@ package net.Indyuce.mmocore.api.loot; -import java.util.Optional; -import java.util.Random; +import java.util.stream.Stream; import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; @@ -14,27 +13,28 @@ public class RegionBounds { private final World world; private final int x1, z1, x2, z2; - private static final Random random = new Random(); - public RegionBounds(ConfigurationSection config) { Validate.notNull(config, "Could not load config"); - Validate.notNull(world = Bukkit.getWorld(config.getString("world")), - "Could not find world " + config.getString("world")); - x1 = config.getInt("x1"); - z1 = config.getInt("z1"); + Validate.notNull(world = Bukkit.getWorld(config.getString("world")), "Could not find world " + config.getString("world")); + x1 = Math.min(config.getInt("x1"), config.getInt("x2")); + x2 = Math.max(config.getInt("x1"), config.getInt("x2")); - x2 = config.getInt("x2"); - z2 = config.getInt("z2"); + z1 = Math.min(config.getInt("z1"), config.getInt("z2")); + z2 = Math.max(config.getInt("z1"), config.getInt("z2")); } public RegionBounds(Location loc1, Location loc2) { Validate.isTrue(loc1.getWorld().equals(loc2.getWorld()), "Locations must be in the same world"); world = loc1.getWorld(); - x1 = loc1.getBlockX(); - z1 = loc1.getBlockZ(); + x1 = Math.min(loc1.getBlockX(), loc2.getBlockX()); + x2 = Math.max(loc1.getBlockX(), loc2.getBlockX()); - x2 = loc2.getBlockX(); - z2 = loc2.getBlockZ(); + z1 = Math.min(loc1.getBlockZ(), loc2.getBlockZ()); + z2 = Math.max(loc1.getBlockZ(), loc2.getBlockZ()); + } + + public Stream getPlayers() { + return world.getPlayers().stream().filter(player -> isInRegion(player)); } public boolean isInRegion(Player player) { @@ -42,12 +42,4 @@ public class RegionBounds { int z = player.getLocation().getBlockZ(); return player.getWorld().equals(world) && x1 <= x && x2 >= x && z1 <= z && z2 >= z; } - - public Location findChestLocation() { - - Optional player = world.getPlayers().stream().filter(check -> isInRegion(check)).findAny(); - - // TODO - return null; - } } diff --git a/src/main/java/net/Indyuce/mmocore/api/loot/TierEffect.java b/src/main/java/net/Indyuce/mmocore/api/loot/TierEffect.java new file mode 100644 index 00000000..03cbdd68 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/api/loot/TierEffect.java @@ -0,0 +1,37 @@ +package net.Indyuce.mmocore.api.loot; + +import org.apache.commons.lang.Validate; +import org.bukkit.Location; +import org.bukkit.Particle; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.scheduler.BukkitRunnable; + +import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.util.math.particle.ChestParticleEffect; + +public class TierEffect { + private final ChestParticleEffect type; + private final Particle particle; + private final int period; + + public TierEffect(ConfigurationSection config) { + Validate.notNull(config, "Could not load tier config"); + type = ChestParticleEffect.valueOf(config.getString("type", "OFFSET").toUpperCase().replace("-", "_").replace(" ", "_")); + particle = Particle.valueOf(config.getString("particle", "FLAME").toUpperCase().replace("-", "_").replace(" ", "_")); + period = Math.max(20, config.getInt("period", 5 * 20)); + } + + public void play(Location loc) { + type.play(loc, particle); + } + + public BukkitRunnable startNewRunnable(Location loc) { + BukkitRunnable runnable = new BukkitRunnable() { + public void run() { + type.play(loc, particle); + } + }; + runnable.runTaskTimer(MMOCore.plugin, 0, period); + return runnable; + } +} \ No newline at end of file diff --git a/src/main/java/net/Indyuce/mmocore/api/player/social/Party.java b/src/main/java/net/Indyuce/mmocore/api/player/social/Party.java index d5c7379d..6e99c540 100644 --- a/src/main/java/net/Indyuce/mmocore/api/player/social/Party.java +++ b/src/main/java/net/Indyuce/mmocore/api/player/social/Party.java @@ -20,19 +20,23 @@ public class Party { private final PartyMembers members = new PartyMembers(); private final Map invites = new HashMap<>(); + // used to check if two parties are the same + private final UUID id = UUID.randomUUID(); + /* * owner changes when the old owner leaves party */ private PlayerData owner; - // used to check if two parties are the same - // private UUID uuid = UUID.randomUUID(); - public Party(PlayerData owner) { this.owner = owner; addMember(owner); } + public UUID getUniqueId() { + return id; + } + public PlayerData getOwner() { return owner; } @@ -50,7 +54,8 @@ public class Party { } public void removeMember(PlayerData data) { - if (data.isOnline() && data.getPlayer().getOpenInventory() != null && data.getPlayer().getOpenInventory().getTopInventory().getHolder() instanceof PartyViewInventory) + if (data.isOnline() && data.getPlayer().getOpenInventory() != null + && data.getPlayer().getOpenInventory().getTopInventory().getHolder() instanceof PartyViewInventory) InventoryManager.PARTY_CREATION.newInventory(data).open(); members.remove(data); @@ -83,20 +88,22 @@ public class Party { public void reopenInventories() { for (PlayerData member : members.members) - if (member.isOnline() && member.getPlayer().getOpenInventory() != null && member.getPlayer().getOpenInventory().getTopInventory().getHolder() instanceof PartyViewInventory) + if (member.isOnline() && member.getPlayer().getOpenInventory() != null + && member.getPlayer().getOpenInventory().getTopInventory().getHolder() instanceof PartyViewInventory) ((PluginInventory) member.getPlayer().getOpenInventory().getTopInventory().getHolder()).open(); } public void sendPartyInvite(PlayerData inviter, PlayerData target) { invites.put(target.getUniqueId(), System.currentTimeMillis()); Request request = new PartyInvite(this, inviter, target); - new ConfigMessage("party-invite").addPlaceholders("player", inviter.getPlayer().getName(), "uuid", request.getUniqueId().toString()).sendAsJSon(target.getPlayer()); + new ConfigMessage("party-invite").addPlaceholders("player", inviter.getPlayer().getName(), "uuid", request.getUniqueId().toString()) + .sendAsJSon(target.getPlayer()); MMOCore.plugin.requestManager.registerRequest(request); } @Override public boolean equals(Object obj) { - return obj instanceof Party && ((Party) obj).owner.getUniqueId().equals(owner.getUniqueId()); + return obj instanceof Party && ((Party) obj).getUniqueId().equals(getUniqueId()); } /* @@ -136,7 +143,8 @@ public class Party { } private void applyAttributes(PlayerData player) { - MMOCore.plugin.partyManager.getBonuses().forEach(stat -> player.getStats().getInstance(stat).addModifier("mmocoreParty", MMOCore.plugin.partyManager.getBonus(stat).multiply(members.size() - 1))); + MMOCore.plugin.partyManager.getBonuses().forEach(stat -> player.getStats().getInstance(stat).addModifier("mmocoreParty", + MMOCore.plugin.partyManager.getBonus(stat).multiply(members.size() - 1))); } private void clearAttributes(PlayerData player) { diff --git a/src/main/java/net/Indyuce/mmocore/api/player/stats/PlayerStats.java b/src/main/java/net/Indyuce/mmocore/api/player/stats/PlayerStats.java index ed8416cb..18b2e1f2 100644 --- a/src/main/java/net/Indyuce/mmocore/api/player/stats/PlayerStats.java +++ b/src/main/java/net/Indyuce/mmocore/api/player/stats/PlayerStats.java @@ -38,11 +38,11 @@ public class PlayerStats { * applies relative attributes on the base stat too */ public double getStat(StatType stat) { - return getInstance(stat).getTotal(getBase(stat)); + return getInstance(stat).getTotal(); } public double getBase(StatType stat) { - return data.getProfess().calculateStat(stat, stat.hasProfession() ? data.getCollectionSkills().getLevel(stat.getProfession()) : data.getLevel()); + return getInstance(stat).getBase(); } /* 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 449ff797..535b4820 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 @@ -51,6 +51,12 @@ public enum StatType { PROJECTILE_DAMAGE, WEAPON_DAMAGE, SKILL_DAMAGE, + + DAMAGE_REDUCTION, + PHYSICAL_DAMAGE_REDUCTION, + PROJECTILE_DAMAGE_REDUCTION, + WEAPON_DAMAGE_REDUCTION, + SKILL_DAMAGE_REDUCTION, // reduces amount of tugs needed to fish FISHING_STRENGTH("fishing"), diff --git a/src/main/java/net/Indyuce/mmocore/api/skill/result/LocationSkillResult.java b/src/main/java/net/Indyuce/mmocore/api/skill/result/LocationSkillResult.java index 34d6c31a..4a6bf89b 100644 --- a/src/main/java/net/Indyuce/mmocore/api/skill/result/LocationSkillResult.java +++ b/src/main/java/net/Indyuce/mmocore/api/skill/result/LocationSkillResult.java @@ -20,7 +20,7 @@ public class LocationSkillResult extends SkillResult { if (isSuccessful()) { - RayTraceResult result = data.getPlayer().getWorld().rayTrace(data.getPlayer().getEyeLocation(), data.getPlayer().getEyeLocation().getDirection(), range, FluidCollisionMode.ALWAYS, true, 1.0D, entity -> MMOCoreUtils.canTarget(data.getPlayer(), entity)); + RayTraceResult result = data.getPlayer().getWorld().rayTrace(data.getPlayer().getEyeLocation(), data.getPlayer().getEyeLocation().getDirection(), range, FluidCollisionMode.ALWAYS, true, 1.0D, entity -> MMOCoreUtils.canTarget(data, entity)); if (result == null) abort(CancelReason.OTHER); else diff --git a/src/main/java/net/Indyuce/mmocore/api/skill/result/TargetSkillResult.java b/src/main/java/net/Indyuce/mmocore/api/skill/result/TargetSkillResult.java index 1d3f5cfe..e920d6ca 100644 --- a/src/main/java/net/Indyuce/mmocore/api/skill/result/TargetSkillResult.java +++ b/src/main/java/net/Indyuce/mmocore/api/skill/result/TargetSkillResult.java @@ -16,7 +16,7 @@ public class TargetSkillResult extends SkillResult { super(data, skill); if (isSuccessful()) { - MMORayTraceResult result = MMOLib.plugin.getVersion().getWrapper().rayTrace(data.getPlayer(), range, entity -> MMOCoreUtils.canTarget(data.getPlayer(), entity)); + MMORayTraceResult result = MMOLib.plugin.getVersion().getWrapper().rayTrace(data.getPlayer(), range, entity -> MMOCoreUtils.canTarget(data, entity)); if (!result.hasHit()) abort(CancelReason.OTHER); else diff --git a/src/main/java/net/Indyuce/mmocore/api/util/math/formula/RandomAmount.java b/src/main/java/net/Indyuce/mmocore/api/util/math/formula/RandomAmount.java index 32d2a714..87975926 100644 --- a/src/main/java/net/Indyuce/mmocore/api/util/math/formula/RandomAmount.java +++ b/src/main/java/net/Indyuce/mmocore/api/util/math/formula/RandomAmount.java @@ -3,7 +3,7 @@ package net.Indyuce.mmocore.api.util.math.formula; import java.util.Random; public class RandomAmount { - private double min, max; + private final double min, max; private static final Random random = new Random(); @@ -15,8 +15,7 @@ public class RandomAmount { public RandomAmount(String value) { String[] split = value.split("\\-"); min = Double.parseDouble(split[0]); - if (split.length > 1) - max = Double.parseDouble(split[1]); + max = split.length > 1 ? Double.parseDouble(split[1]) : 0; } public double getMax() { diff --git a/src/main/java/net/Indyuce/mmocore/listener/LootableChestsListener.java b/src/main/java/net/Indyuce/mmocore/listener/LootableChestsListener.java index 147bdeb8..bde1ae20 100644 --- a/src/main/java/net/Indyuce/mmocore/listener/LootableChestsListener.java +++ b/src/main/java/net/Indyuce/mmocore/listener/LootableChestsListener.java @@ -6,7 +6,7 @@ import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryCloseEvent; import net.Indyuce.mmocore.MMOCore; -import net.Indyuce.mmocore.manager.LootableChestManager.LootableChest; +import net.Indyuce.mmocore.api.loot.LootChest; public class LootableChestsListener implements Listener { @EventHandler @@ -15,8 +15,8 @@ public class LootableChestsListener implements Listener { return; Chest chest = (Chest) event.getInventory().getHolder(); - LootableChest lootable = MMOCore.plugin.chestManager.getLootableChest(chest.getLocation()); - if (lootable != null) - lootable.whenClosed(true); + LootChest lootChest = MMOCore.plugin.lootChests.getChest(chest.getLocation()); + if (lootChest != null) + lootChest.unregister(true); } } diff --git a/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java b/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java index 3e64039f..488c58ac 100644 --- a/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java +++ b/src/main/java/net/Indyuce/mmocore/manager/ConfigManager.java @@ -27,7 +27,7 @@ public class ConfigManager { public double expPartyBuff, regenPartyBuff; public String partyChatPrefix; public ChatColor manaFull, manaHalf, manaEmpty, staminaFull, staminaHalf, staminaEmpty; - public int combatLogTimer; + public int combatLogTimer, lootChestExpireTime; public final DecimalFormatSymbols formatSymbols = new DecimalFormatSymbols(); public final DecimalFormat decimal = new DecimalFormat("0.#", formatSymbols), decimals = new DecimalFormat("0.##", formatSymbols); @@ -86,7 +86,7 @@ public class ConfigManager { loadDefaultFile("stats.yml"); loadDefaultFile("waypoints.yml"); loadDefaultFile("restrictions.yml"); - loadDefaultFile("chests.yml"); + // loadDefaultFile("chests.yml"); loadDefaultFile("commands.yml"); loadDefaultFile("guilds.yml"); @@ -100,6 +100,7 @@ public class ConfigManager { partyChatPrefix = MMOCore.plugin.getConfig().getString("party.chat-prefix"); formatSymbols.setDecimalSeparator(getFirstChar(MMOCore.plugin.getConfig().getString("number-format.decimal-separator"), ',')); combatLogTimer = MMOCore.plugin.getConfig().getInt("combat-log.timer"); + lootChestExpireTime = Math.max(MMOCore.plugin.getConfig().getInt("loot-chest-expire-time"), 1) * 1000; manaFull = getColorOrDefault("mana-whole", ChatColor.BLUE); manaHalf = getColorOrDefault("mana-half", ChatColor.AQUA); diff --git a/src/main/java/net/Indyuce/mmocore/manager/DropTableManager.java b/src/main/java/net/Indyuce/mmocore/manager/DropTableManager.java index ca2b4a81..5a703a33 100644 --- a/src/main/java/net/Indyuce/mmocore/manager/DropTableManager.java +++ b/src/main/java/net/Indyuce/mmocore/manager/DropTableManager.java @@ -15,7 +15,7 @@ import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.droptable.DropTable; public class DropTableManager extends MMOManager { - private Map map = new HashMap<>(); + private final Map map = new HashMap<>(); public void register(DropTable table) { map.put(table.getId(), table); @@ -47,11 +47,8 @@ public class DropTableManager extends MMOManager { if (obj instanceof String) return get((String) obj); - if (obj instanceof ConfigurationSection) { - DropTable table = new DropTable((ConfigurationSection) obj); - table.load(); - return table; - } + if (obj instanceof ConfigurationSection) + return new DropTable((ConfigurationSection) obj).load(); throw new IllegalArgumentException("Could not parse drop table."); } diff --git a/src/main/java/net/Indyuce/mmocore/manager/LootChestManager.java b/src/main/java/net/Indyuce/mmocore/manager/LootChestManager.java new file mode 100644 index 00000000..787a8947 --- /dev/null +++ b/src/main/java/net/Indyuce/mmocore/manager/LootChestManager.java @@ -0,0 +1,75 @@ +package net.Indyuce.mmocore.manager; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; + +import org.bukkit.Location; +import org.bukkit.configuration.file.FileConfiguration; + +import net.Indyuce.mmocore.MMOCore; +import net.Indyuce.mmocore.api.ConfigFile; +import net.Indyuce.mmocore.api.loot.LootChest; +import net.Indyuce.mmocore.api.loot.LootChestRegion; + +public class LootChestManager { + + /* + * all active loot chests in the server + */ + private final Set active = new HashSet<>(); + + private final Map regions = new HashMap<>(); + + public boolean hasRegion(String id) { + return regions.containsKey(id); + } + + public LootChestRegion getRegion(String id) { + return regions.get(id); + } + + public Collection getRegions() { + return regions.values(); + } + + public Set getActive() { + return active; + } + + public void register(LootChest chest) { + active.add(chest); + } + + public void unregister(LootChest chest) { + active.remove(chest); + } + + public LootChest getChest(Location loc) { + + for (LootChest chest : active) + if (chest.getBlock().matches(loc)) + return chest; + + return null; + } + + public void reload() { + regions.values().forEach(region -> region.getRunnable().cancel()); + regions.clear(); + + FileConfiguration config = new ConfigFile("loot-chests").getConfig(); + for (String key : config.getKeys(false)) + try { + LootChestRegion region = new LootChestRegion(config.getConfigurationSection(key)); + regions.put(region.getId(), region); + } catch (IllegalArgumentException exception) { + MMOCore.plugin.getLogger().log(Level.WARNING, + "An error occured while trying to load loot chest region '" + key + "': " + exception.getMessage()); + } + + } +} diff --git a/src/main/java/net/Indyuce/mmocore/manager/LootableChestManager.java b/src/main/java/net/Indyuce/mmocore/manager/LootableChestManager.java deleted file mode 100644 index 15134998..00000000 --- a/src/main/java/net/Indyuce/mmocore/manager/LootableChestManager.java +++ /dev/null @@ -1,169 +0,0 @@ -package net.Indyuce.mmocore.manager; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Random; -import java.util.Set; -import java.util.logging.Level; - -import org.apache.commons.lang.Validate; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Particle; -import org.bukkit.Sound; -import org.bukkit.World; -import org.bukkit.block.Chest; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitRunnable; - -import net.Indyuce.mmocore.MMOCore; -import net.Indyuce.mmocore.api.droptable.DropTable; -import net.Indyuce.mmocore.api.util.math.particle.ChestParticleEffect; - -public class LootableChestManager { - private Set map = new HashSet<>(); - - private static BukkitRunnable runnable; - private static final Random random = new Random(); - - public LootableChestManager(FileConfiguration config) { - for (String key : config.getKeys(false)) - try { - register(new LootableChest(config.getConfigurationSection(key))); - } catch (IllegalArgumentException | IndexOutOfBoundsException exception) { - MMOCore.plugin.getLogger().log(Level.WARNING, "Could not register loot chest '" + key + "': " + exception.getMessage()); - } - - if (runnable != null) - runnable.cancel(); - - (runnable = new BukkitRunnable() { - public void run() { - map.forEach(chest -> { - if (chest.hasEffect() && chest.isSpawned() && chest.hasPlayerNearby()) - chest.playEffect(); - }); - } - }).runTaskTimerAsynchronously(MMOCore.plugin, 100, 4 * 20); - } - - public void register(LootableChest chest) { - map.add(chest); - chest.whenClosed(false); - } - - public LootableChest getLootableChest(Location loc) { - for (LootableChest chest : map) - if (blockCheck(chest.getLocation(), loc)) - return chest; - return null; - } - - private boolean blockCheck(Location loc1, Location loc2) { - return loc1.getWorld().equals(loc2.getWorld()) && loc1.getBlockX() == loc2.getBlockX() && loc1.getBlockY() == loc2.getBlockY() && loc1.getBlockZ() == loc2.getBlockZ(); - } - - public class LootableChest { - private final Location loc; - private final DropTable table; - private final int regenTime; - private final Particle effectParticle; - private final ChestParticleEffect effect; - - private long lastDisappear; - - public LootableChest(ConfigurationSection config) { - loc = readLocation(config.getName()); - regenTime = config.getInt("regen-time"); - table = MMOCore.plugin.dropTableManager.loadDropTable(config.get("drop-table")); - - if (config.contains("effect")) { - String format = config.getString("effect.particle"); - Validate.notNull(format, "Particle is missing particle"); - effectParticle = Particle.valueOf(format.toUpperCase().replace("-", "_")); - - format = config.getString("effect.type"); - Validate.notNull(format, "Particle is missing effect type"); - effect = ChestParticleEffect.valueOf(format.toUpperCase().replace("-", "_")); - - } else { - effectParticle = null; - effect = null; - } - } - - public boolean hasEffect() { - return effectParticle != null && effect != null; - } - - public boolean isSpawned() { - return System.currentTimeMillis() > lastDisappear + 50 * regenTime; - } - - public Location getLocation() { - return loc; - } - - public DropTable getDropTable() { - return table; - } - - public int getRegenTime() { - return regenTime; - } - - public void playEffect() { - effect.play(loc.clone().add(.5, .5, .5), effectParticle); - } - - public void whenClosed(boolean sound) { - if (sound) { - loc.getWorld().playSound(loc, Sound.ITEM_ARMOR_EQUIP_LEATHER, 1, 1); - loc.getWorld().spawnParticle(Particle.CRIT, loc.clone().add(.5, .5, .5), 16, 0, 0, 0, .5); - } - if (loc.getBlock().getState() instanceof Chest) - ((Chest) loc.getBlock().getState()).getBlockInventory().clear(); - loc.getBlock().setType(Material.AIR); - lastDisappear = System.currentTimeMillis(); - Bukkit.getScheduler().scheduleSyncDelayedTask(MMOCore.plugin, () -> whenSpawn(), regenTime); - } - - public boolean hasPlayerNearby() { - for (Player player : loc.getWorld().getPlayers()) - if (player.getLocation().distanceSquared(loc) < 625) - return true; - return false; - } - - public void whenSpawn() { - List slots = new ArrayList<>(); - for (int j = 0; j < 27; j++) - slots.add(j); - - loc.getBlock().setType(Material.CHEST); - Chest chest = (Chest) loc.getBlock().getState(); - table.collect().forEach(item -> { - Integer slot = slots.get(random.nextInt(slots.size())); - chest.getInventory().setItem(slot, item); - slots.remove(slot); - }); - } - - private Location readLocation(String string) { - String[] split = string.split("\\ "); - - World world = Bukkit.getWorld(split[0]); - Validate.notNull(world, "Could not find world '" + split[0] + "'"); - - double x = Double.parseDouble(split[1]); - double y = Double.parseDouble(split[2]); - double z = Double.parseDouble(split[3]); - - return new Location(world, x, y, z); - } - } -} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 794f4b44..5f5de156 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -34,14 +34,22 @@ lootsplosion: offset: .2 height: .6 +# Time in seconds it takes for a loot chest to +# expire after it was spawned. 600 is 10 minutes. +loot-chest-expire-time: 600 + # Settings for the default action bar action-bar: + # Whether or not to use the default action bar. (This doesn't change any other action bars provided by MMOCore.) enabled: true + # The decimal format for stats (not including stat formats in stats.yml) decimal: "0.#" + # The amount of ticks before updating the info ticks-to-update: 5 + # How to display the data. format: "&c❤ {health}/{max_health} &f| &9⭐ {mana}/{max_mana} &f| &7⛨ {armor}" diff --git a/src/main/resources/default/professions/mining.yml b/src/main/resources/default/professions/mining.yml index dac0324c..8be90143 100644 --- a/src/main/resources/default/professions/mining.yml +++ b/src/main/resources/default/professions/mining.yml @@ -35,8 +35,6 @@ on-mine: temp-block: 'skull{value="eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTU0ODE4MjMzYzgxMTg3M2U4NWY1YTRlYTQ0MjliNzVmMjNiNmFlMGVhNmY1ZmMwZjdiYjQyMGQ3YzQ3MSJ9fX0="}' triggers: - 'exp{profession=mining;amount=20}' - - emerald: material: vanilla{type=EMERALD_ORE}