2018-11-06 04:33:10 +01:00
|
|
|
package com.songoda.ultimatestacker.tasks;
|
|
|
|
|
|
|
|
import com.songoda.ultimatestacker.UltimateStacker;
|
|
|
|
import com.songoda.ultimatestacker.entity.EntityStack;
|
|
|
|
import com.songoda.ultimatestacker.entity.EntityStackManager;
|
|
|
|
import com.songoda.ultimatestacker.utils.Methods;
|
2019-05-23 22:15:04 +02:00
|
|
|
import com.songoda.ultimatestacker.utils.settings.Setting;
|
2018-11-06 04:33:10 +01:00
|
|
|
import org.bukkit.Bukkit;
|
|
|
|
import org.bukkit.World;
|
|
|
|
import org.bukkit.configuration.ConfigurationSection;
|
2019-06-15 11:05:53 +02:00
|
|
|
import org.bukkit.entity.ArmorStand;
|
|
|
|
import org.bukkit.entity.Entity;
|
2019-07-19 02:49:41 +02:00
|
|
|
import org.bukkit.entity.HumanEntity;
|
2019-06-15 11:05:53 +02:00
|
|
|
import org.bukkit.entity.LivingEntity;
|
2018-11-06 04:33:10 +01:00
|
|
|
import org.bukkit.scheduler.BukkitRunnable;
|
|
|
|
|
2018-11-08 04:26:38 +01:00
|
|
|
import java.util.ArrayList;
|
2019-01-15 05:45:25 +01:00
|
|
|
import java.util.Collections;
|
2018-11-06 04:33:10 +01:00
|
|
|
import java.util.List;
|
2019-01-15 06:08:51 +01:00
|
|
|
import java.util.UUID;
|
2019-07-19 02:49:41 +02:00
|
|
|
import java.util.stream.Collectors;
|
2018-11-06 04:33:10 +01:00
|
|
|
|
|
|
|
public class StackingTask extends BukkitRunnable {
|
|
|
|
|
|
|
|
private final UltimateStacker instance;
|
|
|
|
|
2019-06-27 20:34:33 +02:00
|
|
|
private EntityStackManager stackManager;
|
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
ConfigurationSection configurationSection = UltimateStacker.getInstance().getMobFile().getConfig();
|
|
|
|
|
|
|
|
private List<UUID> processed = new ArrayList<>();
|
2019-06-27 20:34:33 +02:00
|
|
|
|
|
|
|
|
2018-11-06 04:33:10 +01:00
|
|
|
public StackingTask(UltimateStacker instance) {
|
|
|
|
this.instance = instance;
|
2019-06-27 20:34:33 +02:00
|
|
|
this.stackManager = instance.getEntityStackManager();
|
2019-03-21 04:36:58 +01:00
|
|
|
|
|
|
|
// Start stacking task.
|
2019-05-23 22:15:04 +02:00
|
|
|
runTaskTimer(instance, 0, Setting.STACK_SEARCH_TICK_SPEED.getInt());
|
2018-11-06 04:33:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2019-07-19 02:49:41 +02:00
|
|
|
// Gather disabled worlds.
|
2019-05-23 22:15:04 +02:00
|
|
|
List<String> disabledWorlds = Setting.DISABLED_WORLDS.getStringList();
|
2018-11-06 04:33:10 +01:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Should entities be stacked?
|
|
|
|
if (!Setting.STACK_ENTITIES.getBoolean()) return;
|
|
|
|
|
|
|
|
// Loop through each world.
|
2018-11-06 04:33:10 +01:00
|
|
|
for (World world : Bukkit.getWorlds()) {
|
2019-07-19 02:49:41 +02:00
|
|
|
// If world is disabled then continue to the next world.
|
2019-04-11 08:54:59 +02:00
|
|
|
if (disabledWorlds.stream().anyMatch(worldStr -> world.getName().equalsIgnoreCase(worldStr))) continue;
|
2018-11-06 04:33:10 +01:00
|
|
|
|
2019-07-19 04:19:33 +02:00
|
|
|
// Get the loaded entities from the current world and reverse them.
|
|
|
|
List<Entity> entities = new ArrayList<>(world.getEntities());
|
2019-01-15 05:45:25 +01:00
|
|
|
Collections.reverse(entities);
|
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Loop through the entities.
|
|
|
|
for (Entity entity : entities) {
|
2019-07-19 04:19:33 +02:00
|
|
|
// Check to see if entity is stackable.
|
|
|
|
if (!isEntityStackable(entity)) continue;
|
2019-07-19 02:49:41 +02:00
|
|
|
// Make sure our entity has not already been processed.
|
|
|
|
// Skip it if it has been.
|
|
|
|
if (this.processed.contains(entity.getUniqueId())) continue;
|
2019-06-23 00:28:47 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Cast our entity to living entity.
|
|
|
|
LivingEntity livingEntity = (LivingEntity) entity;
|
2018-11-08 04:26:38 +01:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Process the entity.
|
|
|
|
this.processEntity(livingEntity);
|
2018-11-06 04:33:10 +01:00
|
|
|
|
2019-06-27 20:34:33 +02:00
|
|
|
}
|
|
|
|
}
|
2019-07-19 02:49:41 +02:00
|
|
|
processed.clear();
|
2019-06-27 20:34:33 +02:00
|
|
|
}
|
2018-11-06 04:33:10 +01:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
private boolean isEntityStackable(Entity entity) {
|
|
|
|
// Make sure we have the correct entity type and that it is valid.
|
|
|
|
if (!entity.isValid()
|
|
|
|
|| !(entity instanceof LivingEntity)
|
|
|
|
|| entity instanceof HumanEntity
|
|
|
|
|| entity instanceof ArmorStand
|
2019-06-28 05:14:40 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Make sure the entity is not in love.
|
|
|
|
|| entity.hasMetadata("inLove")
|
|
|
|
// Or in breeding cooldown.
|
|
|
|
|| entity.hasMetadata("breedCooldown")
|
|
|
|
|
|
|
|
// If only stack from spawners is enabled make sure the entity spawned from a spawner.
|
|
|
|
|| Setting.ONLY_STACK_FROM_SPAWNERS.getBoolean()
|
|
|
|
&& !(entity.hasMetadata("US_REASON")
|
|
|
|
&& entity.getMetadata("US_REASON").get(0).asString().equals("SPAWNER")))
|
2019-06-28 05:14:40 +02:00
|
|
|
return false;
|
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Cast our entity to living entity.
|
|
|
|
LivingEntity livingEntity = (LivingEntity) entity;
|
2019-06-15 11:05:53 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// If only stack on surface is enabled make sure the entity is on a surface then entity is stackable.
|
|
|
|
return !Setting.ONLY_STACK_ON_SURFACE.getBoolean()
|
|
|
|
|| Methods.canFly(livingEntity)
|
|
|
|
|| (livingEntity.isOnGround() || livingEntity.getLocation().getBlock().isLiquid());
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
}
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
private void processEntity(LivingEntity livingEntity) {
|
|
|
|
// Get the stack from the entity. It should be noted that this value will
|
|
|
|
// be null if our entity is not a stack.
|
|
|
|
EntityStack stack = instance.getEntityStackManager().getStack(livingEntity);
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Is this entity stacked?
|
|
|
|
boolean isStack = stack != null;
|
2018-11-06 04:33:10 +01:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// The amount that is stackable.
|
|
|
|
int amountToStack = isStack ? stack.getAmount() : 1;
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Attempt to split our stack. If the split is successful then skip this entity.
|
|
|
|
if (isStack && attemptSplit(stack, livingEntity)) return;
|
|
|
|
|
|
|
|
// If this entity is named or disabled then skip it.
|
|
|
|
if (!isStack && livingEntity.getCustomName() != null
|
|
|
|
|| !configurationSection.getBoolean("Mobs." + livingEntity.getType().name() + ".Enabled"))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Get the minimum stack size.
|
|
|
|
int minEntityStackSize = Setting.MIN_STACK_ENTITIES.getInt();
|
|
|
|
// Get the maximum stack size for this entity.
|
|
|
|
int maxEntityStackSize = getEntityStackSize(livingEntity);
|
|
|
|
|
|
|
|
// Get similar entities around our entity and make sure those entities are both compatible and stackable.
|
|
|
|
List<LivingEntity> stackableFriends = Methods.getSimilarEntitiesAroundEntity(livingEntity)
|
|
|
|
.stream().filter(this::isEntityStackable).collect(Collectors.toList());
|
|
|
|
|
|
|
|
// Loop through our similar stackable entities.
|
|
|
|
for (LivingEntity entity : stackableFriends) {
|
|
|
|
// Make sure the entity has not already been processed.
|
|
|
|
if (this.processed.contains(entity.getUniqueId())) continue;
|
|
|
|
|
|
|
|
// Get this entities friendStack.
|
|
|
|
EntityStack friendStack = stackManager.getStack(entity);
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Check to see if this entity is stacked and friendStack plus
|
|
|
|
// our amount to stack is not above our max friendStack size
|
|
|
|
// for this entity.
|
|
|
|
if (friendStack != null && (friendStack.getAmount() + amountToStack) <= maxEntityStackSize) {
|
2019-06-30 01:32:16 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Add one to the found friendStack.
|
|
|
|
friendStack.addAmount(amountToStack);
|
|
|
|
|
|
|
|
// Add our entities health to the friendStacks health.
|
|
|
|
friendStack.addHealth(entity.getHealth());
|
|
|
|
|
|
|
|
// Fix the friendStacks health.
|
|
|
|
if (!isStack)
|
|
|
|
friendStack.addHealth(entity.getHealth());
|
2019-06-30 01:32:16 +02:00
|
|
|
else
|
2019-07-19 02:49:41 +02:00
|
|
|
friendStack.mergeHealth(stack);
|
2019-06-30 01:32:16 +02:00
|
|
|
|
2019-06-28 22:00:30 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
fixHealth(entity, livingEntity);
|
2019-06-30 02:16:01 +02:00
|
|
|
if (Setting.STACK_ENTITY_HEALTH.getBoolean())
|
2019-07-19 02:49:41 +02:00
|
|
|
entity.setHealth(entity.getMaxHealth() < livingEntity.getHealth()
|
|
|
|
? entity.getMaxHealth() : livingEntity.getHealth());
|
|
|
|
|
2019-06-28 22:00:30 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Remove our entity and mark it as processed.
|
|
|
|
livingEntity.remove();
|
|
|
|
processed.add(livingEntity.getUniqueId());
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
return;
|
|
|
|
} else if (friendStack == null
|
|
|
|
&& isStack
|
|
|
|
&& (stack.getAmount() + 1) <= maxEntityStackSize
|
2019-06-27 20:34:33 +02:00
|
|
|
&& Methods.canFly(entity)
|
|
|
|
&& Setting.ONLY_STACK_FLYING_DOWN.getBoolean()
|
2019-07-19 02:49:41 +02:00
|
|
|
&& livingEntity.getLocation().getY() > entity.getLocation().getY()) {
|
2019-06-30 01:32:16 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// If entity has a custom name skip it.
|
|
|
|
if (entity.getCustomName() != null) continue;
|
2019-06-30 02:16:01 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Create a new stack with the current stacks amount and add one to it.
|
|
|
|
EntityStack newStack = stackManager.addStack(entity, stack.getAmount() + 1);
|
2019-06-28 22:00:30 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Fix the entities health.
|
|
|
|
newStack.mergeHealth(stack);
|
|
|
|
newStack.addHealth(livingEntity.getHealth());
|
|
|
|
fixHealth(livingEntity, entity);
|
2019-06-30 02:16:01 +02:00
|
|
|
if (Setting.STACK_ENTITY_HEALTH.getBoolean())
|
|
|
|
entity.setHealth(entity.getHealth());
|
2019-06-28 22:00:30 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Remove our entities stack from the stack manager.
|
|
|
|
stackManager.removeStack(livingEntity);
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Remove our entity and mark it as processed.
|
|
|
|
livingEntity.remove();
|
|
|
|
processed.add(livingEntity.getUniqueId());
|
|
|
|
|
|
|
|
return;
|
2018-11-06 04:33:10 +01:00
|
|
|
}
|
|
|
|
}
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// If our entity is stacked then skip this entity.
|
|
|
|
if (isStack) return;
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Remove all stacked entities from our stackable friends.
|
|
|
|
stackableFriends.removeIf(stackManager::isStacked);
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// If there are none or not enough stackable friends left to create a new entity,
|
|
|
|
// the stack sizes overlap then skip this entity.
|
|
|
|
if (stackableFriends.isEmpty()
|
|
|
|
|| stackableFriends.size() < minEntityStackSize - 1
|
|
|
|
|| minEntityStackSize > maxEntityStackSize) return;
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// If a stack was never found create a new one.
|
|
|
|
EntityStack newStack = stackManager.addStack(new EntityStack(livingEntity, (stackableFriends.size() + 1) >
|
|
|
|
maxEntityStackSize ? maxEntityStackSize : stackableFriends.size() + 1));
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Loop through the unstacked and unprocessed stackable friends while not creating
|
|
|
|
// a stack larger than the maximum.
|
|
|
|
stackableFriends.stream().filter(entity -> !stackManager.isStacked(entity)
|
|
|
|
&& !this.processed.contains(entity.getUniqueId())).limit(maxEntityStackSize).forEach(entity -> {
|
2019-06-28 22:00:30 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Fix the entities health.
|
|
|
|
fixHealth(livingEntity, entity);
|
|
|
|
newStack.addHealth(entity.getHealth());
|
2019-06-28 22:00:30 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Remove our entity and mark it as processed.
|
2019-06-27 20:34:33 +02:00
|
|
|
entity.remove();
|
2019-07-19 02:49:41 +02:00
|
|
|
processed.add(entity.getUniqueId());
|
2019-06-27 20:34:33 +02:00
|
|
|
});
|
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Update our stacks health.
|
|
|
|
updateHealth(newStack);
|
|
|
|
|
|
|
|
// Update our stack.
|
|
|
|
newStack.updateStack();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void updateHealth(EntityStack stack) {
|
|
|
|
if (Setting.STACK_ENTITY_HEALTH.getBoolean())
|
|
|
|
stack.updateHealth(stack.getEntity());
|
2018-11-06 04:33:10 +01:00
|
|
|
}
|
2019-06-23 02:10:22 +02:00
|
|
|
|
2019-07-19 02:50:07 +02:00
|
|
|
|
|
|
|
public boolean attemptSplit(EntityStack stack, LivingEntity livingEntity) {
|
2019-07-17 14:56:50 +02:00
|
|
|
int stackSize = stack.getAmount();
|
2019-07-19 02:50:07 +02:00
|
|
|
int maxEntityStackAmount = getEntityStackSize(livingEntity);
|
2019-07-17 14:56:50 +02:00
|
|
|
|
2019-07-19 02:50:07 +02:00
|
|
|
if (stackSize <= maxEntityStackAmount) return false;
|
2019-07-19 02:50:29 +02:00
|
|
|
|
2019-07-19 02:50:07 +02:00
|
|
|
for (int i = stackSize; i > 0; i -= maxEntityStackAmount)
|
|
|
|
this.processed.add(instance.getEntityStackManager()
|
|
|
|
.addStack(Methods.newEntity(livingEntity), i > maxEntityStackAmount ? maxEntityStackAmount : i).getEntityUniqueId());
|
2019-07-17 14:56:50 +02:00
|
|
|
|
2019-07-19 02:50:07 +02:00
|
|
|
// Remove our entities stack from the stack manager.
|
|
|
|
stackManager.removeStack(livingEntity);
|
|
|
|
|
|
|
|
// Remove our entity and mark it as processed.
|
|
|
|
livingEntity.remove();
|
|
|
|
processed.add(livingEntity.getUniqueId());
|
|
|
|
return true;
|
2019-07-17 14:56:50 +02:00
|
|
|
}
|
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
|
|
|
|
|
2019-06-28 22:00:30 +02:00
|
|
|
private void fixHealth(LivingEntity entity, LivingEntity initialEntity) {
|
2019-06-30 01:32:16 +02:00
|
|
|
if (!Setting.STACK_ENTITY_HEALTH.getBoolean() && Setting.CARRY_OVER_LOWEST_HEALTH.getBoolean() && initialEntity.getHealth() < entity.getHealth())
|
2019-06-28 22:00:30 +02:00
|
|
|
entity.setHealth(initialEntity.getHealth());
|
|
|
|
}
|
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
private int getEntityStackSize(LivingEntity initialEntity) {
|
|
|
|
int maxEntityStackSize = Setting.MAX_STACK_ENTITIES.getInt();
|
|
|
|
if (configurationSection.getInt("Mobs." + initialEntity.getType().name() + ".Max Stack Size") != -1)
|
|
|
|
maxEntityStackSize = configurationSection.getInt("Mobs." + initialEntity.getType().name() + ".Max Stack Size");
|
|
|
|
return maxEntityStackSize;
|
2019-06-30 01:32:16 +02:00
|
|
|
}
|
2019-07-19 02:49:41 +02:00
|
|
|
|
2018-11-06 04:33:10 +01:00
|
|
|
}
|