!API needed for loot chests

This commit is contained in:
Indyuce 2020-04-14 01:07:57 +02:00
parent 25b350c09a
commit d8af4ba8bc
34 changed files with 629 additions and 327 deletions

Binary file not shown.

View File

@ -2,6 +2,7 @@ package net.Indyuce.mmocore;
import java.io.File; import java.io.File;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.logging.Level; import java.util.logging.Level;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -13,6 +14,7 @@ import org.bukkit.scheduler.BukkitRunnable;
import net.Indyuce.mmocore.api.ConfigFile; import net.Indyuce.mmocore.api.ConfigFile;
import net.Indyuce.mmocore.api.PlayerActionBar; import net.Indyuce.mmocore.api.PlayerActionBar;
import net.Indyuce.mmocore.api.loot.LootChest;
import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.player.profess.resource.PlayerResource; import net.Indyuce.mmocore.api.player.profess.resource.PlayerResource;
import net.Indyuce.mmocore.api.player.social.guilds.Guild; import net.Indyuce.mmocore.api.player.social.guilds.Guild;
@ -72,7 +74,7 @@ import net.Indyuce.mmocore.manager.DropTableManager;
import net.Indyuce.mmocore.manager.EntityManager; import net.Indyuce.mmocore.manager.EntityManager;
import net.Indyuce.mmocore.manager.ExperienceManager; import net.Indyuce.mmocore.manager.ExperienceManager;
import net.Indyuce.mmocore.manager.InventoryManager; import net.Indyuce.mmocore.manager.InventoryManager;
import net.Indyuce.mmocore.manager.LootableChestManager; import net.Indyuce.mmocore.manager.LootChestManager;
import net.Indyuce.mmocore.manager.MMOLoadManager; import net.Indyuce.mmocore.manager.MMOLoadManager;
import net.Indyuce.mmocore.manager.QuestManager; import net.Indyuce.mmocore.manager.QuestManager;
import net.Indyuce.mmocore.manager.RestrictionManager; import net.Indyuce.mmocore.manager.RestrictionManager;
@ -101,7 +103,6 @@ public class MMOCore extends JavaPlugin {
public ConfigManager configManager; public ConfigManager configManager;
public WaypointManager waypointManager; public WaypointManager waypointManager;
public RestrictionManager restrictionManager; public RestrictionManager restrictionManager;
public LootableChestManager chestManager;
public RequestManager requestManager; public RequestManager requestManager;
public ConfigItemManager configItems; public ConfigItemManager configItems;
public SkillManager skillManager; public SkillManager skillManager;
@ -122,6 +123,7 @@ public class MMOCore extends JavaPlugin {
public final ProfessionManager professionManager = new ProfessionManager(); public final ProfessionManager professionManager = new ProfessionManager();
public final EntityManager entities = new EntityManager(); public final EntityManager entities = new EntityManager();
public final ExperienceManager experience = new ExperienceManager(); public final ExperienceManager experience = new ExperienceManager();
public final LootChestManager lootChests = new LootChestManager();
/* /*
* professions * professions
@ -235,6 +237,20 @@ public class MMOCore extends JavaPlugin {
} }
}.runTaskTimer(MMOCore.plugin, 100, 20); }.runTaskTimer(MMOCore.plugin, 100, 20);
/*
* clean active loot chests every 5 minutes. cannot register this
* runnable in the loot chest manager because it is instanced when the
* plugin loads
*/
new BukkitRunnable() {
public void run() {
for (LootChest chest : new HashSet<>(lootChests.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 * For the sake of the lord, make sure they aren't using MMOItems Mana
* and Stamina Addon...This should prevent a couple error reports * and Stamina Addon...This should prevent a couple error reports
@ -360,6 +376,8 @@ public class MMOCore extends JavaPlugin {
dataProvider.getGuildManager().save(guild); dataProvider.getGuildManager().save(guild);
mineManager.resetRemainingBlocks(); mineManager.resetRemainingBlocks();
lootChests.getActive().forEach(chest -> chest.unregister(false));
} }
public void reloadPlugin() { public void reloadPlugin() {
@ -396,7 +414,8 @@ public class MMOCore extends JavaPlugin {
questManager.clear(); questManager.clear();
questManager.reload(); questManager.reload();
chestManager = new LootableChestManager(new ConfigFile("chests").getConfig()); lootChests.reload();
waypointManager = new WaypointManager(new ConfigFile("waypoints").getConfig()); waypointManager = new WaypointManager(new ConfigFile("waypoints").getConfig());
restrictionManager = new RestrictionManager(new ConfigFile("restrictions").getConfig()); restrictionManager = new RestrictionManager(new ConfigFile("restrictions").getConfig());
requestManager = new RequestManager(); requestManager = new RequestManager();

View File

@ -13,6 +13,7 @@ import org.bukkit.inventory.ItemStack;
import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.droptable.DropTable; import net.Indyuce.mmocore.api.droptable.DropTable;
import net.Indyuce.mmocore.api.load.MMOLoadException; 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.ExperienceTrigger;
import net.Indyuce.mmocore.api.quest.trigger.Trigger; import net.Indyuce.mmocore.api.quest.trigger.Trigger;
import net.mmogroup.mmolib.api.MMOLineConfig; import net.mmogroup.mmolib.api.MMOLineConfig;
@ -76,8 +77,8 @@ public class BlockInfo {
return table; return table;
} }
public List<ItemStack> collectDrops() { public List<ItemStack> collectDrops(LootBuilder builder) {
return hasDropTable() ? table.collect() : new ArrayList<>(); return hasDropTable() ? table.collect(builder) : new ArrayList<>();
} }
public boolean hasDropTable() { public boolean hasDropTable() {

View File

@ -1,7 +1,6 @@
package net.Indyuce.mmocore.api.droptable; package net.Indyuce.mmocore.api.droptable;
import java.util.ArrayList; import java.util.LinkedHashSet;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -12,11 +11,12 @@ import org.bukkit.inventory.ItemStack;
import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.droptable.dropitem.DropItem; import net.Indyuce.mmocore.api.droptable.dropitem.DropItem;
import net.Indyuce.mmocore.api.load.MMOLoadException; import net.Indyuce.mmocore.api.load.MMOLoadException;
import net.Indyuce.mmocore.api.loot.LootBuilder;
import net.mmogroup.mmolib.api.MMOLineConfig; import net.mmogroup.mmolib.api.MMOLineConfig;
public class DropTable { public class DropTable {
private final String id; private final String id;
private final Set<DropItem> drops = new HashSet<>(); private final Set<DropItem> drops = new LinkedHashSet<>();
/* /*
* cached in order to load other items. * cached in order to load other items.
@ -52,13 +52,14 @@ public class DropTable {
return id; return id;
} }
public List<ItemStack> collect() { public List<ItemStack> collect(LootBuilder builder) {
List<ItemStack> total = new ArrayList<>();
for (DropItem item : drops) for (DropItem item : drops)
if (item.rollChance()) if (item.rollChance() && builder.getCapacity() >= item.getWeight()) {
item.collect(total); item.collect(builder);
builder.reduceCapacity(item.getWeight());
}
return total; return builder.getLoot();
} }
} }

View File

@ -1,22 +1,21 @@
package net.Indyuce.mmocore.api.droptable.dropitem; package net.Indyuce.mmocore.api.droptable.dropitem;
import java.util.List;
import java.util.Random; 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.Indyuce.mmocore.api.util.math.formula.RandomAmount;
import net.mmogroup.mmolib.api.MMOLineConfig; import net.mmogroup.mmolib.api.MMOLineConfig;
public abstract class DropItem { public abstract class DropItem {
protected static final Random random = new Random(); protected static final Random random = new Random();
private double chance; private final double chance, weight;
private RandomAmount amount; private final RandomAmount amount;
public DropItem(MMOLineConfig config) { public DropItem(MMOLineConfig config) {
chance = config.args().length > 0 ? Double.parseDouble(config.args()[0]) : 1; 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); 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() { public RandomAmount getAmount() {
@ -27,6 +26,10 @@ public abstract class DropItem {
return chance; return chance;
} }
public double getWeight() {
return weight;
}
public int rollAmount() { public int rollAmount() {
return amount.calculateInt(); return amount.calculateInt();
} }
@ -35,5 +38,5 @@ public abstract class DropItem {
return random.nextDouble() < chance; return random.nextDouble() < chance;
} }
public abstract void collect(List<ItemStack> total); public abstract void collect(LootBuilder builder);
} }

View File

@ -1,16 +1,14 @@
package net.Indyuce.mmocore.api.droptable.dropitem; package net.Indyuce.mmocore.api.droptable.dropitem;
import java.util.List;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.inventory.ItemStack;
import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.droptable.DropTable; import net.Indyuce.mmocore.api.droptable.DropTable;
import net.Indyuce.mmocore.api.loot.LootBuilder;
import net.mmogroup.mmolib.api.MMOLineConfig; import net.mmogroup.mmolib.api.MMOLineConfig;
public class DropTableDropItem extends DropItem { public class DropTableDropItem extends DropItem {
private DropTable dropTable; private final DropTable dropTable;
public DropTableDropItem(MMOLineConfig config) { public DropTableDropItem(MMOLineConfig config) {
super(config); super(config);
@ -23,8 +21,8 @@ public class DropTableDropItem extends DropItem {
} }
@Override @Override
public void collect(List<ItemStack> total) { public void collect(LootBuilder builder) {
for (int j = 0; j < rollAmount(); j++) for (int j = 0; j < rollAmount(); j++)
total.addAll(dropTable.collect()); builder.addLoot(dropTable.collect(builder));
} }
} }

View File

@ -1,9 +1,6 @@
package net.Indyuce.mmocore.api.droptable.dropitem; package net.Indyuce.mmocore.api.droptable.dropitem;
import java.util.List; import net.Indyuce.mmocore.api.loot.LootBuilder;
import org.bukkit.inventory.ItemStack;
import net.Indyuce.mmocore.api.util.item.CurrencyItem; import net.Indyuce.mmocore.api.util.item.CurrencyItem;
import net.mmogroup.mmolib.api.MMOLineConfig; import net.mmogroup.mmolib.api.MMOLineConfig;
@ -13,7 +10,7 @@ public class GoldDropItem extends DropItem {
} }
@Override @Override
public void collect(List<ItemStack> total) { public void collect(LootBuilder builder) {
total.add(new CurrencyItem("GOLD_COIN", 1, rollAmount()).build()); builder.addLoot(new CurrencyItem("GOLD_COIN", 1, rollAmount()).build());
} }
} }

View File

@ -1,10 +1,6 @@
package net.Indyuce.mmocore.api.droptable.dropitem; package net.Indyuce.mmocore.api.droptable.dropitem;
import java.util.List;
import java.util.NoSuchElementException; 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.MythicMobs;
import io.lumine.xikage.mythicmobs.adapters.bukkit.BukkitAdapter; 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.DropTable;
import io.lumine.xikage.mythicmobs.drops.IItemDrop; import io.lumine.xikage.mythicmobs.drops.IItemDrop;
import io.lumine.xikage.mythicmobs.drops.LootBag; 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; import net.mmogroup.mmolib.api.MMOLineConfig;
public class MMDropTableDropItem extends DropItem { public class MMDropTableDropItem extends DropItem {
private DropTable dropTable; private final DropTable dropTable;
private DropMetadata metadata = new DropMetadata(null, null);
private static final DropMetadata metadata = new DropMetadata(null, null);
public MMDropTableDropItem(MMOLineConfig config) { public MMDropTableDropItem(MMOLineConfig config) {
super(config); super(config);
@ -28,19 +25,16 @@ public class MMDropTableDropItem extends DropItem {
try { try {
dropTable = MythicMobs.inst().getDropManager().getDropTable(id).get(); dropTable = MythicMobs.inst().getDropManager().getDropTable(id).get();
} catch(NoSuchElementException e) { } catch (NoSuchElementException exception) {
MMOCore.log(Level.WARNING, "Could not find MM drop table" + id); throw new IllegalArgumentException("Could not find MM drop table with ID '" + id + "'");
} }
} }
@Override @Override
public void collect(List<ItemStack> total) { public void collect(LootBuilder builder) {
LootBag lootBag = dropTable.generate(metadata); LootBag lootBag = dropTable.generate(metadata);
for (Drop type : lootBag.getDrops())
for(Drop type : lootBag.getDrops()) { if (type instanceof IItemDrop)
if(type instanceof IItemDrop) { builder.addLoot(BukkitAdapter.adapt(((IItemDrop) type).getDrop(metadata)));
total.add(BukkitAdapter.adapt(((IItemDrop)type).getDrop(metadata)));
}
}
} }
} }

View File

@ -1,14 +1,11 @@
package net.Indyuce.mmocore.api.droptable.dropitem; package net.Indyuce.mmocore.api.droptable.dropitem;
import java.util.List; import net.Indyuce.mmocore.api.loot.LootBuilder;
import org.bukkit.inventory.ItemStack;
import net.Indyuce.mmocore.api.util.item.CurrencyItem; import net.Indyuce.mmocore.api.util.item.CurrencyItem;
import net.mmogroup.mmolib.api.MMOLineConfig; import net.mmogroup.mmolib.api.MMOLineConfig;
public class NoteDropItem extends DropItem { public class NoteDropItem extends DropItem {
private int min, max; private final int min, max;
public NoteDropItem(MMOLineConfig config) { public NoteDropItem(MMOLineConfig config) {
super(config); super(config);
@ -20,7 +17,7 @@ public class NoteDropItem extends DropItem {
} }
@Override @Override
public void collect(List<ItemStack> total) { public void collect(LootBuilder builder) {
total.add(new CurrencyItem("NOTE", random.nextInt(max - min + 1) + min, rollAmount()).build()); builder.addLoot(new CurrencyItem("NOTE", random.nextInt(max - min + 1) + min, rollAmount()).build());
} }
} }

View File

@ -1,10 +1,9 @@
package net.Indyuce.mmocore.api.droptable.dropitem; package net.Indyuce.mmocore.api.droptable.dropitem;
import java.util.List;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import net.Indyuce.mmocore.api.loot.LootBuilder;
import net.mmogroup.mmolib.api.MMOLineConfig; import net.mmogroup.mmolib.api.MMOLineConfig;
public class VanillaDropItem extends DropItem { public class VanillaDropItem extends DropItem {
@ -22,7 +21,7 @@ public class VanillaDropItem extends DropItem {
} }
@Override @Override
public void collect(List<ItemStack> total) { public void collect(LootBuilder builder) {
total.add(new ItemStack(material, rollAmount())); builder.addLoot(new ItemStack(material, rollAmount()));
} }
} }

View File

@ -1,12 +1,10 @@
package net.Indyuce.mmocore.api.droptable.dropitem.fishing; package net.Indyuce.mmocore.api.droptable.dropitem.fishing;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.droptable.dropitem.DropItem; 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.Indyuce.mmocore.api.util.math.formula.RandomAmount;
import net.mmogroup.mmolib.api.MMOLineConfig; import net.mmogroup.mmolib.api.MMOLineConfig;
@ -58,8 +56,8 @@ public class FishingDropItem {
} }
public ItemStack collect() { public ItemStack collect() {
List<ItemStack> collect = new ArrayList<>(); LootBuilder builder = new LootBuilder(null, 0);
dropItem.collect(collect); dropItem.collect(builder);
return collect.stream().findAny().get(); return builder.getLoot().stream().findAny().get();
} }
} }

View File

@ -9,6 +9,7 @@ import org.bukkit.inventory.ItemStack;
import net.Indyuce.mmocore.api.block.BlockInfo; import net.Indyuce.mmocore.api.block.BlockInfo;
import net.Indyuce.mmocore.api.experience.ExperienceInfo; import net.Indyuce.mmocore.api.experience.ExperienceInfo;
import net.Indyuce.mmocore.api.loot.LootBuilder;
import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.api.player.PlayerData;
public class CustomBlockMineEvent extends PlayerDataEvent implements Cancellable { public class CustomBlockMineEvent extends PlayerDataEvent implements Cancellable {
@ -24,7 +25,7 @@ public class CustomBlockMineEvent extends PlayerDataEvent implements Cancellable
super(player); super(player);
this.block = block; this.block = block;
this.drops = info.collectDrops(); this.drops = info.collectDrops(new LootBuilder(player, 0));
this.experience = info.hasExperience() ? info.getExperience().newInfo() : null; this.experience = info.hasExperience() ? info.getExperience().newInfo() : null;
} }

View File

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

View File

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

View File

@ -1,24 +1,32 @@
package net.Indyuce.mmocore.api.loot; 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.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 { public class ChestTier {
private final TierEffect effect; private final TierEffect effect;
private final int weight; private final ScalingFormula capacity;
private final DropTable table;
public final double chance;
public ChestTier(ConfigurationSection config) { public ChestTier(ConfigurationSection config) {
effect = config.isConfigurationSection("effect") ? new TierEffect(config.getConfigurationSection("effect")) effect = config.isConfigurationSection("effect") ? new TierEffect(config.getConfigurationSection("effect")) : null;
: null; capacity = new ScalingFormula(config.get("capacity"));
weight = config.getInt("weight", 1); chance = config.getDouble("chance");
table = MMOCore.plugin.dropTableManager.loadDropTable(config.get("drops"));
} }
public int getWeight() { public double rollCapacity(PlayerData player) {
return weight; return capacity.calculate(player.getLevel());
}
public DropTable getDropTable() {
return table;
} }
public boolean hasEffect() { public boolean hasEffect() {
@ -28,21 +36,4 @@ public class ChestTier {
public TierEffect getEffect() { public TierEffect getEffect() {
return effect; 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);
}
}
} }

View File

@ -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<ItemStack> 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<ItemStack> getLoot() {
return loot;
}
public double getCapacity() {
return capacity;
}
public void addLoot(ItemStack item) {
loot.add(item);
}
public void addLoot(List<? extends ItemStack> items) {
loot.addAll(items);
}
public void reduceCapacity(double value) {
this.capacity = Math.max(0, capacity - value);
}
}

View File

@ -2,20 +2,37 @@ package net.Indyuce.mmocore.api.loot;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.Chest;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import net.Indyuce.mmocore.MMOCore;
public class LootChest { public class LootChest {
private final ChestTier tier;
private final LootChestRegion region; 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(ChestTier tier, LootChestRegion region, Block block) {
this.tier = tier;
public LootChest(LootChestRegion region, Block block) {
this.region = region; this.region = region;
this.block = new ReplacedBlock(block); 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() { public ReplacedBlock getBlock() {
@ -26,6 +43,40 @@ public class LootChest {
return region; 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 { public class ReplacedBlock {
private final Material material; private final Material material;
private final BlockData data; private final BlockData data;
@ -34,7 +85,16 @@ public class LootChest {
public ReplacedBlock(Block block) { public ReplacedBlock(Block block) {
this.material = block.getType(); this.material = block.getType();
this.data = block.getBlockData(); 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() { public void restore() {

View File

@ -1,31 +1,44 @@
package net.Indyuce.mmocore.api.loot; 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.Set;
import java.util.logging.Level; import java.util.logging.Level;
import org.apache.commons.lang.Validate; 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.configuration.ConfigurationSection;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;
import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.event.LootChestSpawnEvent;
import net.Indyuce.mmocore.api.player.PlayerData;
public class LootChestRegion { public class LootChestRegion {
private final String id; private final String id;
private final long chestSpawnPeriod; private final long chestSpawnPeriod;
private final RegionBounds bounds; private final RegionBounds bounds;
private final Set<ChestTier> tiers = new HashSet<>(); private final ChestAlgorithmOptions algOptions;
private final Set<ChestTier> tiers = new LinkedHashSet<>();
private final BukkitRunnable runnable;
/* private static final Random random = new Random();
* last time
*/
public LootChestRegion(ConfigurationSection config) { public LootChestRegion(ConfigurationSection config) {
Validate.notNull(config, "Could not load config"); Validate.notNull(config, "Could not load config");
id = config.getName(); id = config.getName().toLowerCase().replace("_", "-").replace(" ", "-");
bounds = new RegionBounds(config.getConfigurationSection("bounds")); 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"); Validate.isTrue(config.isConfigurationSection("tiers"), "Could not find chest tiers");
for (String key : config.getConfigurationSection("tiers").getKeys(false)) for (String key : config.getConfigurationSection("tiers").getKeys(false))
@ -35,6 +48,10 @@ public class LootChestRegion {
MMOCore.plugin.getLogger().log(Level.WARNING, MMOCore.plugin.getLogger().log(Level.WARNING,
"Could not load tier '" + key + "' from chest region '" + id + "': " + exception.getMessage()); "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() { public String getId() {
@ -52,4 +69,101 @@ public class LootChestRegion {
public long getChestSpawnPeriod() { public long getChestSpawnPeriod() {
return chestSpawnPeriod; 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<Integer> 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();
}
} }

View File

@ -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<Player> found = region.getBounds().getPlayers().findAny();
if (found.isPresent())
region.spawnChest(PlayerData.get(found.get()));
}
}

View File

@ -1,7 +1,6 @@
package net.Indyuce.mmocore.api.loot; package net.Indyuce.mmocore.api.loot;
import java.util.Optional; import java.util.stream.Stream;
import java.util.Random;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -14,27 +13,28 @@ public class RegionBounds {
private final World world; private final World world;
private final int x1, z1, x2, z2; private final int x1, z1, x2, z2;
private static final Random random = new Random();
public RegionBounds(ConfigurationSection config) { public RegionBounds(ConfigurationSection config) {
Validate.notNull(config, "Could not load config"); Validate.notNull(config, "Could not load config");
Validate.notNull(world = Bukkit.getWorld(config.getString("world")), Validate.notNull(world = Bukkit.getWorld(config.getString("world")), "Could not find world " + config.getString("world"));
"Could not find world " + config.getString("world")); x1 = Math.min(config.getInt("x1"), config.getInt("x2"));
x1 = config.getInt("x1"); x2 = Math.max(config.getInt("x1"), config.getInt("x2"));
z1 = config.getInt("z1");
x2 = config.getInt("x2"); z1 = Math.min(config.getInt("z1"), config.getInt("z2"));
z2 = config.getInt("z2"); z2 = Math.max(config.getInt("z1"), config.getInt("z2"));
} }
public RegionBounds(Location loc1, Location loc2) { public RegionBounds(Location loc1, Location loc2) {
Validate.isTrue(loc1.getWorld().equals(loc2.getWorld()), "Locations must be in the same world"); Validate.isTrue(loc1.getWorld().equals(loc2.getWorld()), "Locations must be in the same world");
world = loc1.getWorld(); world = loc1.getWorld();
x1 = loc1.getBlockX(); x1 = Math.min(loc1.getBlockX(), loc2.getBlockX());
z1 = loc1.getBlockZ(); x2 = Math.max(loc1.getBlockX(), loc2.getBlockX());
x2 = loc2.getBlockX(); z1 = Math.min(loc1.getBlockZ(), loc2.getBlockZ());
z2 = loc2.getBlockZ(); z2 = Math.max(loc1.getBlockZ(), loc2.getBlockZ());
}
public Stream<Player> getPlayers() {
return world.getPlayers().stream().filter(player -> isInRegion(player));
} }
public boolean isInRegion(Player player) { public boolean isInRegion(Player player) {
@ -42,12 +42,4 @@ public class RegionBounds {
int z = player.getLocation().getBlockZ(); int z = player.getLocation().getBlockZ();
return player.getWorld().equals(world) && x1 <= x && x2 >= x && z1 <= z && z2 >= z; return player.getWorld().equals(world) && x1 <= x && x2 >= x && z1 <= z && z2 >= z;
} }
public Location findChestLocation() {
Optional<Player> player = world.getPlayers().stream().filter(check -> isInRegion(check)).findAny();
// TODO
return null;
}
} }

View File

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

View File

@ -20,19 +20,23 @@ public class Party {
private final PartyMembers members = new PartyMembers(); private final PartyMembers members = new PartyMembers();
private final Map<UUID, Long> invites = new HashMap<>(); private final Map<UUID, Long> 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 * owner changes when the old owner leaves party
*/ */
private PlayerData owner; private PlayerData owner;
// used to check if two parties are the same
// private UUID uuid = UUID.randomUUID();
public Party(PlayerData owner) { public Party(PlayerData owner) {
this.owner = owner; this.owner = owner;
addMember(owner); addMember(owner);
} }
public UUID getUniqueId() {
return id;
}
public PlayerData getOwner() { public PlayerData getOwner() {
return owner; return owner;
} }
@ -50,7 +54,8 @@ public class Party {
} }
public void removeMember(PlayerData data) { 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(); InventoryManager.PARTY_CREATION.newInventory(data).open();
members.remove(data); members.remove(data);
@ -83,20 +88,22 @@ public class Party {
public void reopenInventories() { public void reopenInventories() {
for (PlayerData member : members.members) 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(); ((PluginInventory) member.getPlayer().getOpenInventory().getTopInventory().getHolder()).open();
} }
public void sendPartyInvite(PlayerData inviter, PlayerData target) { public void sendPartyInvite(PlayerData inviter, PlayerData target) {
invites.put(target.getUniqueId(), System.currentTimeMillis()); invites.put(target.getUniqueId(), System.currentTimeMillis());
Request request = new PartyInvite(this, inviter, target); 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); MMOCore.plugin.requestManager.registerRequest(request);
} }
@Override @Override
public boolean equals(Object obj) { 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) { 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) { private void clearAttributes(PlayerData player) {

View File

@ -38,11 +38,11 @@ public class PlayerStats {
* applies relative attributes on the base stat too * applies relative attributes on the base stat too
*/ */
public double getStat(StatType stat) { public double getStat(StatType stat) {
return getInstance(stat).getTotal(getBase(stat)); return getInstance(stat).getTotal();
} }
public double getBase(StatType stat) { public double getBase(StatType stat) {
return data.getProfess().calculateStat(stat, stat.hasProfession() ? data.getCollectionSkills().getLevel(stat.getProfession()) : data.getLevel()); return getInstance(stat).getBase();
} }
/* /*

View File

@ -52,6 +52,12 @@ public enum StatType {
WEAPON_DAMAGE, WEAPON_DAMAGE,
SKILL_DAMAGE, SKILL_DAMAGE,
DAMAGE_REDUCTION,
PHYSICAL_DAMAGE_REDUCTION,
PROJECTILE_DAMAGE_REDUCTION,
WEAPON_DAMAGE_REDUCTION,
SKILL_DAMAGE_REDUCTION,
// reduces amount of tugs needed to fish // reduces amount of tugs needed to fish
FISHING_STRENGTH("fishing"), FISHING_STRENGTH("fishing"),

View File

@ -20,7 +20,7 @@ public class LocationSkillResult extends SkillResult {
if (isSuccessful()) { 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) if (result == null)
abort(CancelReason.OTHER); abort(CancelReason.OTHER);
else else

View File

@ -16,7 +16,7 @@ public class TargetSkillResult extends SkillResult {
super(data, skill); super(data, skill);
if (isSuccessful()) { 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()) if (!result.hasHit())
abort(CancelReason.OTHER); abort(CancelReason.OTHER);
else else

View File

@ -3,7 +3,7 @@ package net.Indyuce.mmocore.api.util.math.formula;
import java.util.Random; import java.util.Random;
public class RandomAmount { public class RandomAmount {
private double min, max; private final double min, max;
private static final Random random = new Random(); private static final Random random = new Random();
@ -15,8 +15,7 @@ public class RandomAmount {
public RandomAmount(String value) { public RandomAmount(String value) {
String[] split = value.split("\\-"); String[] split = value.split("\\-");
min = Double.parseDouble(split[0]); min = Double.parseDouble(split[0]);
if (split.length > 1) max = split.length > 1 ? Double.parseDouble(split[1]) : 0;
max = Double.parseDouble(split[1]);
} }
public double getMax() { public double getMax() {

View File

@ -6,7 +6,7 @@ import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.inventory.InventoryCloseEvent;
import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.manager.LootableChestManager.LootableChest; import net.Indyuce.mmocore.api.loot.LootChest;
public class LootableChestsListener implements Listener { public class LootableChestsListener implements Listener {
@EventHandler @EventHandler
@ -15,8 +15,8 @@ public class LootableChestsListener implements Listener {
return; return;
Chest chest = (Chest) event.getInventory().getHolder(); Chest chest = (Chest) event.getInventory().getHolder();
LootableChest lootable = MMOCore.plugin.chestManager.getLootableChest(chest.getLocation()); LootChest lootChest = MMOCore.plugin.lootChests.getChest(chest.getLocation());
if (lootable != null) if (lootChest != null)
lootable.whenClosed(true); lootChest.unregister(true);
} }
} }

View File

@ -27,7 +27,7 @@ public class ConfigManager {
public double expPartyBuff, regenPartyBuff; public double expPartyBuff, regenPartyBuff;
public String partyChatPrefix; public String partyChatPrefix;
public ChatColor manaFull, manaHalf, manaEmpty, staminaFull, staminaHalf, staminaEmpty; public ChatColor manaFull, manaHalf, manaEmpty, staminaFull, staminaHalf, staminaEmpty;
public int combatLogTimer; public int combatLogTimer, lootChestExpireTime;
public final DecimalFormatSymbols formatSymbols = new DecimalFormatSymbols(); public final DecimalFormatSymbols formatSymbols = new DecimalFormatSymbols();
public final DecimalFormat decimal = new DecimalFormat("0.#", formatSymbols), decimals = new DecimalFormat("0.##", formatSymbols); public final DecimalFormat decimal = new DecimalFormat("0.#", formatSymbols), decimals = new DecimalFormat("0.##", formatSymbols);
@ -86,7 +86,7 @@ public class ConfigManager {
loadDefaultFile("stats.yml"); loadDefaultFile("stats.yml");
loadDefaultFile("waypoints.yml"); loadDefaultFile("waypoints.yml");
loadDefaultFile("restrictions.yml"); loadDefaultFile("restrictions.yml");
loadDefaultFile("chests.yml"); // loadDefaultFile("chests.yml");
loadDefaultFile("commands.yml"); loadDefaultFile("commands.yml");
loadDefaultFile("guilds.yml"); loadDefaultFile("guilds.yml");
@ -100,6 +100,7 @@ public class ConfigManager {
partyChatPrefix = MMOCore.plugin.getConfig().getString("party.chat-prefix"); partyChatPrefix = MMOCore.plugin.getConfig().getString("party.chat-prefix");
formatSymbols.setDecimalSeparator(getFirstChar(MMOCore.plugin.getConfig().getString("number-format.decimal-separator"), ',')); formatSymbols.setDecimalSeparator(getFirstChar(MMOCore.plugin.getConfig().getString("number-format.decimal-separator"), ','));
combatLogTimer = MMOCore.plugin.getConfig().getInt("combat-log.timer"); 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); manaFull = getColorOrDefault("mana-whole", ChatColor.BLUE);
manaHalf = getColorOrDefault("mana-half", ChatColor.AQUA); manaHalf = getColorOrDefault("mana-half", ChatColor.AQUA);

View File

@ -15,7 +15,7 @@ import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.droptable.DropTable; import net.Indyuce.mmocore.api.droptable.DropTable;
public class DropTableManager extends MMOManager { public class DropTableManager extends MMOManager {
private Map<String, DropTable> map = new HashMap<>(); private final Map<String, DropTable> map = new HashMap<>();
public void register(DropTable table) { public void register(DropTable table) {
map.put(table.getId(), table); map.put(table.getId(), table);
@ -47,11 +47,8 @@ public class DropTableManager extends MMOManager {
if (obj instanceof String) if (obj instanceof String)
return get((String) obj); return get((String) obj);
if (obj instanceof ConfigurationSection) { if (obj instanceof ConfigurationSection)
DropTable table = new DropTable((ConfigurationSection) obj); return new DropTable((ConfigurationSection) obj).load();
table.load();
return table;
}
throw new IllegalArgumentException("Could not parse drop table."); throw new IllegalArgumentException("Could not parse drop table.");
} }

View File

@ -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<LootChest> active = new HashSet<>();
private final Map<String, LootChestRegion> regions = new HashMap<>();
public boolean hasRegion(String id) {
return regions.containsKey(id);
}
public LootChestRegion getRegion(String id) {
return regions.get(id);
}
public Collection<LootChestRegion> getRegions() {
return regions.values();
}
public Set<LootChest> 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());
}
}
}

View File

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

View File

@ -34,14 +34,22 @@ lootsplosion:
offset: .2 offset: .2
height: .6 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 # Settings for the default action bar
action-bar: action-bar:
# Whether or not to use the default action bar. (This doesn't change any other action bars provided by MMOCore.) # Whether or not to use the default action bar. (This doesn't change any other action bars provided by MMOCore.)
enabled: true enabled: true
# The decimal format for stats (not including stat formats in stats.yml) # The decimal format for stats (not including stat formats in stats.yml)
decimal: "0.#" decimal: "0.#"
# The amount of ticks before updating the info # The amount of ticks before updating the info
ticks-to-update: 5 ticks-to-update: 5
# How to display the data. # How to display the data.
format: "&c❤ {health}/{max_health} &f| &9⭐ {mana}/{max_mana} &f| &7⛨ {armor}" format: "&c❤ {health}/{max_health} &f| &9⭐ {mana}/{max_mana} &f| &7⛨ {armor}"

View File

@ -36,8 +36,6 @@ on-mine:
triggers: triggers:
- 'exp{profession=mining;amount=20}' - 'exp{profession=mining;amount=20}'
emerald: emerald:
material: vanilla{type=EMERALD_ORE} material: vanilla{type=EMERALD_ORE}
drop-table: drop-table: