2023-05-25 19:20:03 +02:00
|
|
|
package com.craftaro.ultimatestacker.tasks;
|
|
|
|
|
2023-06-30 22:28:37 +02:00
|
|
|
import com.craftaro.core.compatibility.ServerVersion;
|
|
|
|
import com.craftaro.core.hooks.WorldGuardHook;
|
|
|
|
import com.craftaro.core.world.SWorld;
|
2024-03-12 20:25:22 +01:00
|
|
|
import com.craftaro.third_party.com.cryptomorin.xseries.XMaterial;
|
2023-05-25 19:20:03 +02:00
|
|
|
import com.craftaro.ultimatestacker.UltimateStacker;
|
|
|
|
import com.craftaro.ultimatestacker.api.stack.entity.EntityStack;
|
|
|
|
import com.craftaro.ultimatestacker.api.stack.entity.EntityStackManager;
|
|
|
|
import com.craftaro.ultimatestacker.settings.Settings;
|
|
|
|
import com.craftaro.ultimatestacker.stackable.entity.Check;
|
|
|
|
import com.craftaro.ultimatestacker.stackable.entity.custom.CustomEntity;
|
|
|
|
import com.craftaro.ultimatestacker.utils.CachedChunk;
|
2024-03-12 21:21:28 +01:00
|
|
|
import org.bukkit.Bukkit;
|
|
|
|
import org.bukkit.Location;
|
|
|
|
import org.bukkit.Material;
|
|
|
|
import org.bukkit.World;
|
2018-11-06 04:33:10 +01:00
|
|
|
import org.bukkit.configuration.ConfigurationSection;
|
2024-03-12 03:16:05 +01:00
|
|
|
import org.bukkit.entity.*;
|
2020-08-25 01:01:11 +02:00
|
|
|
import org.bukkit.inventory.EntityEquipment;
|
2024-03-12 20:25:22 +01:00
|
|
|
import org.bukkit.scheduler.BukkitRunnable;
|
2018-11-06 04:33:10 +01:00
|
|
|
|
2024-03-12 20:25:22 +01:00
|
|
|
import java.util.*;
|
2024-03-12 21:07:38 +01:00
|
|
|
import java.util.concurrent.CompletableFuture;
|
|
|
|
import java.util.concurrent.ExecutionException;
|
|
|
|
import java.util.concurrent.Future;
|
2024-02-02 15:58:40 +01:00
|
|
|
import java.util.stream.Collectors;
|
2018-11-06 04:33:10 +01:00
|
|
|
|
2023-05-30 11:21:46 +02:00
|
|
|
import static com.craftaro.ultimatestacker.stackable.entity.Check.getChecks;
|
2023-05-25 19:20:03 +02:00
|
|
|
|
2024-03-12 20:25:22 +01:00
|
|
|
public class StackingTask extends BukkitRunnable {
|
2018-11-06 04:33:10 +01:00
|
|
|
|
2019-07-31 06:29:10 +02:00
|
|
|
private final UltimateStacker plugin;
|
2019-09-10 22:45:26 +02:00
|
|
|
private final EntityStackManager stackManager;
|
2024-03-12 20:25:22 +01:00
|
|
|
private final BreedingTask breedingTask;
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2020-08-25 01:01:11 +02:00
|
|
|
private final ConfigurationSection configurationSection = UltimateStacker.getInstance().getMobFile();
|
2019-09-10 22:45:26 +02:00
|
|
|
private final List<UUID> processed = new ArrayList<>();
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2022-10-26 22:04:01 +02:00
|
|
|
private final Map<EntityType, Integer> entityStackSizes = new HashMap<>();
|
2020-08-25 01:01:11 +02:00
|
|
|
private final int maxEntityStackSize = Settings.MAX_STACK_ENTITIES.getInt(),
|
|
|
|
minEntityStackSize = Settings.MIN_STACK_ENTITIES.getInt(),
|
|
|
|
searchRadius = Settings.SEARCH_RADIUS.getInt(),
|
2024-02-02 15:58:40 +01:00
|
|
|
chunkRadius = Settings.STACK_WHOLE_CHUNK_RADIUS.getInt(),
|
2020-08-25 01:01:11 +02:00
|
|
|
maxPerTypeStacksPerChunk = Settings.MAX_PER_TYPE_STACKS_PER_CHUNK.getInt();
|
|
|
|
private final List<String> disabledWorlds = Settings.DISABLED_WORLDS.getStringList(),
|
|
|
|
stackReasons = Settings.STACK_REASONS.getStringList();
|
2023-05-25 19:20:03 +02:00
|
|
|
private final List<Check> checks = getChecks(Settings.STACK_CHECKS.getStringList());
|
2020-08-25 01:01:11 +02:00
|
|
|
private final boolean stackFlyingDown = Settings.ONLY_STACK_FLYING_DOWN.getBoolean(),
|
|
|
|
stackWholeChunk = Settings.STACK_WHOLE_CHUNK.getBoolean(),
|
|
|
|
weaponsArentEquipment = Settings.WEAPONS_ARENT_EQUIPMENT.getBoolean(),
|
|
|
|
onlyStackFromSpawners = Settings.ONLY_STACK_FROM_SPAWNERS.getBoolean(),
|
|
|
|
onlyStackOnSurface = Settings.ONLY_STACK_ON_SURFACE.getBoolean();
|
2019-08-07 20:24:49 +02:00
|
|
|
|
2024-02-02 15:58:40 +01:00
|
|
|
private final Set<SWorld> loadedWorlds;
|
2021-06-13 17:40:46 +02:00
|
|
|
|
2019-07-31 06:29:10 +02:00
|
|
|
public StackingTask(UltimateStacker plugin) {
|
|
|
|
this.plugin = plugin;
|
2024-03-12 20:25:22 +01:00
|
|
|
stackManager = plugin.getEntityStackManager();
|
|
|
|
breedingTask = plugin.getBreedingTask();
|
|
|
|
|
2021-06-13 17:40:46 +02:00
|
|
|
// Add loaded worlds.
|
2024-02-02 15:58:40 +01:00
|
|
|
loadedWorlds = new HashSet<>();
|
|
|
|
for (World world : Bukkit.getWorlds()) {
|
|
|
|
//Filter disabled worlds to avoid continuous checks in the stacking loop
|
|
|
|
if (isWorldDisabled(world)) continue;
|
2021-06-13 17:40:46 +02:00
|
|
|
loadedWorlds.add(new SWorld(world));
|
2024-02-02 15:58:40 +01:00
|
|
|
}
|
2023-02-04 14:48:13 +01:00
|
|
|
|
2024-03-12 20:25:22 +01:00
|
|
|
int tickRate = Settings.STACK_SEARCH_TICK_SPEED.getInt();
|
2024-03-12 21:07:38 +01:00
|
|
|
runTaskTimerAsynchronously(plugin, tickRate, tickRate);
|
2018-11-06 04:33:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2024-02-02 15:58:40 +01:00
|
|
|
//Make sure to continue the task if any exception occurs
|
2023-02-22 09:59:28 +01:00
|
|
|
try {
|
|
|
|
// Loop through each world.
|
|
|
|
for (SWorld sWorld : loadedWorlds) {
|
|
|
|
List<LivingEntity> entities;
|
2024-02-02 15:58:40 +01:00
|
|
|
// Get the loaded entities from the current world and reverse them.
|
2024-03-12 21:07:38 +01:00
|
|
|
try {
|
|
|
|
entities = getLivingEntitiesSync(sWorld).get();
|
|
|
|
} catch (ExecutionException | InterruptedException ex) {
|
|
|
|
ex.printStackTrace();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2024-02-02 15:58:40 +01:00
|
|
|
//Filter non-stackable entities to improve performance on main thread
|
|
|
|
entities.removeIf(this::isEntityNotStackable);
|
|
|
|
|
2024-03-23 19:17:01 +01:00
|
|
|
List<LivingEntity> remove = new ArrayList<>();
|
|
|
|
for (LivingEntity entity : entities) { //Q: What can cause current modification exception here?
|
2024-02-02 15:58:40 +01:00
|
|
|
// Check our WorldGuard flag.
|
|
|
|
Boolean flag = WorldGuardHook.isEnabled() ? WorldGuardHook.getBooleanFlag(entity.getLocation(), "mob-stacking") : null; //Does this work async?
|
2024-03-23 19:17:01 +01:00
|
|
|
if (flag != null && !flag) {
|
|
|
|
remove.add(entity);
|
|
|
|
}
|
2024-02-02 15:58:40 +01:00
|
|
|
}
|
2024-03-23 19:17:01 +01:00
|
|
|
entities.removeAll(remove);
|
2019-01-15 05:45:25 +01:00
|
|
|
|
2024-03-12 21:07:38 +01:00
|
|
|
Bukkit.getScheduler().runTask(plugin, () -> {
|
|
|
|
// Loop through the entities.
|
|
|
|
for (LivingEntity entity : entities) {
|
|
|
|
// Make sure our entity has not already been processed.
|
|
|
|
// Skip it if it has been.
|
|
|
|
if (processed.contains(entity.getUniqueId())) continue;
|
2023-04-07 21:44:08 +02:00
|
|
|
|
2024-03-12 21:07:38 +01:00
|
|
|
// Get entity location to pass around as its faster this way.
|
|
|
|
Location location = entity.getLocation();
|
2023-04-07 21:44:08 +02:00
|
|
|
|
2024-03-12 21:07:38 +01:00
|
|
|
// Process the entity.
|
|
|
|
processEntity(entity, location, entities);
|
|
|
|
}
|
|
|
|
});
|
2019-06-27 20:34:33 +02:00
|
|
|
}
|
2024-03-12 20:25:22 +01:00
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
2024-02-02 15:58:40 +01:00
|
|
|
} finally {
|
|
|
|
// Make sure we clear the processed list.
|
2023-02-22 09:59:28 +01:00
|
|
|
this.processed.clear();
|
2024-02-02 15:58:40 +01:00
|
|
|
}
|
2019-06-27 20:34:33 +02:00
|
|
|
}
|
2018-11-06 04:33:10 +01:00
|
|
|
|
2024-03-12 21:07:38 +01:00
|
|
|
private Future<List<LivingEntity>> getLivingEntitiesSync(SWorld sWorld) {
|
|
|
|
CompletableFuture<List<LivingEntity>> future = new CompletableFuture<>();
|
|
|
|
Bukkit.getScheduler().scheduleSyncDelayedTask(this.plugin, () -> future.complete(sWorld.getLivingEntities()));
|
|
|
|
return future;
|
|
|
|
}
|
|
|
|
|
2019-07-31 21:47:01 +02:00
|
|
|
public boolean isWorldDisabled(World world) {
|
|
|
|
return disabledWorlds.stream().anyMatch(worldStr -> world.getName().equalsIgnoreCase(worldStr));
|
|
|
|
}
|
|
|
|
|
2024-02-02 15:58:40 +01:00
|
|
|
//Returns true if the entity is not stackable, and it will be removed from the list
|
|
|
|
private boolean isEntityNotStackable(LivingEntity entity) {
|
|
|
|
if (isMaxStack(entity)) return true;
|
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Make sure we have the correct entity type and that it is valid.
|
|
|
|
if (!entity.isValid()
|
|
|
|
|| entity instanceof HumanEntity
|
|
|
|
|| entity instanceof ArmorStand
|
2019-06-28 05:14:40 +02:00
|
|
|
|
2024-03-12 20:25:22 +01:00
|
|
|
// Make sure the entity is not in love or in the breeding queue.
|
|
|
|
|| breedingTask.isInQueue(entity.getUniqueId()))
|
2024-02-02 15:58:40 +01:00
|
|
|
return true;
|
2019-07-19 02:49:41 +02:00
|
|
|
|
2024-03-12 20:25:22 +01:00
|
|
|
if (!configurationSection.getBoolean("Mobs." + entity.getType().name() + ".Enabled"))
|
2024-02-02 15:58:40 +01:00
|
|
|
return true;
|
2023-05-05 10:57:07 +02:00
|
|
|
|
2024-02-02 15:58:40 +01:00
|
|
|
// Allow spawn if stack reasons are set and match, or if from a spawner
|
2019-10-24 00:18:42 +02:00
|
|
|
final String spawnReason = entity.hasMetadata("US_REASON") && !entity.getMetadata("US_REASON").isEmpty()
|
|
|
|
? entity.getMetadata("US_REASON").get(0).asString() : null;
|
2019-08-19 03:48:35 +02:00
|
|
|
List<String> stackReasons;
|
2019-12-11 23:31:32 +01:00
|
|
|
if (onlyStackFromSpawners) {
|
2019-08-19 03:48:35 +02:00
|
|
|
// If only stack from spawners is enabled, make sure the entity spawned from a spawner.
|
2024-03-12 20:25:22 +01:00
|
|
|
if (!"SPAWNER".equals(spawnReason))
|
2024-02-02 15:58:40 +01:00
|
|
|
return true;
|
2023-02-22 09:59:28 +01:00
|
|
|
} else if (!(stackReasons = this.stackReasons).isEmpty() && !stackReasons.contains(spawnReason)) {
|
2019-08-19 03:48:35 +02:00
|
|
|
// Only stack if on the list of events to stack
|
2024-02-02 15:58:40 +01:00
|
|
|
return true;
|
2023-02-22 09:59:28 +01:00
|
|
|
}
|
2019-06-28 05:14:40 +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.
|
2024-02-02 15:58:40 +01:00
|
|
|
//return !onlyStackOnSurface || canFly(entity) || entity.getType().name().equals("SHULKER") || ((entity).isOnGround() || (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13) && (entity).isSwimming()));
|
|
|
|
return onlyStackOnSurface && canFly(entity) && !entity.getType().name().equals("SHULKER") && !entity.isOnGround() && !(ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13) && (entity).isSwimming());
|
2019-07-19 02:49:41 +02:00
|
|
|
}
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2024-03-12 20:25:22 +01:00
|
|
|
private void processEntity(LivingEntity baseEntity, Location location, List<LivingEntity> entities) {
|
2019-07-19 02:49:41 +02:00
|
|
|
// Get the stack from the entity. It should be noted that this value will
|
|
|
|
// be null if our entity is not a stack.
|
2023-05-25 19:20:03 +02:00
|
|
|
EntityStack baseStack = plugin.getEntityStackManager().getStackedEntity(baseEntity);
|
2023-02-04 14:48:13 +01:00
|
|
|
|
|
|
|
// Get the maximum stack size for this entity.
|
2024-02-02 15:58:40 +01:00
|
|
|
int maxEntityStackSize = getEntityMaxStackSize(baseEntity);
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Is this entity stacked?
|
2023-02-04 14:48:13 +01:00
|
|
|
boolean isStack = baseStack != null;
|
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// The amount that is stackable.
|
2024-02-02 15:58:40 +01:00
|
|
|
int baseSize = isStack ? baseStack.getAmount() : 1;
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2024-02-02 15:58:40 +01:00
|
|
|
// Attempt to split overstacked entities.
|
|
|
|
// If this is successful, we can return because the entity was processed
|
|
|
|
if (isStack && attemptSplit(baseStack, maxEntityStackSize)) {
|
|
|
|
return;
|
|
|
|
}
|
2019-07-19 02:49:41 +02:00
|
|
|
|
2024-02-02 15:58:40 +01:00
|
|
|
// If this entity is named or a custom entity skip it.
|
|
|
|
if (!isStack && (baseEntity.getCustomName() != null && plugin.getCustomEntityManager().getCustomEntity(baseEntity) != null)) {
|
2023-02-22 09:59:28 +01:00
|
|
|
processed.add(baseEntity.getUniqueId());
|
2019-07-19 02:49:41 +02:00
|
|
|
return;
|
2023-02-22 09:59:28 +01:00
|
|
|
}
|
2019-07-19 02:49:41 +02:00
|
|
|
|
|
|
|
// Get similar entities around our entity and make sure those entities are both compatible and stackable.
|
2024-02-02 15:58:40 +01:00
|
|
|
List<LivingEntity> stackableFriends = getSimilarEntitiesAroundEntity(baseEntity, location);
|
|
|
|
|
|
|
|
//Total entities that can be stacked into the base entity
|
|
|
|
int maxStackable = maxEntityStackSize - baseSize;
|
|
|
|
int toStack = 0;
|
|
|
|
List<LivingEntity> remove = new ArrayList<>();
|
2019-07-19 02:49:41 +02:00
|
|
|
|
|
|
|
// Loop through our similar stackable entities.
|
2023-02-04 14:48:13 +01:00
|
|
|
for (LivingEntity friendlyEntity : stackableFriends) {
|
2019-09-18 18:02:35 +02:00
|
|
|
|
2024-03-12 20:25:22 +01:00
|
|
|
if (!entities.contains(friendlyEntity))
|
2024-02-07 16:03:11 +01:00
|
|
|
continue;
|
|
|
|
|
2024-02-02 15:58:40 +01:00
|
|
|
// Process similar entities.
|
2023-05-25 19:20:03 +02:00
|
|
|
EntityStack friendStack = stackManager.getStackedEntity(friendlyEntity);
|
2023-02-22 09:59:28 +01:00
|
|
|
int amount = friendStack != null ? friendStack.getAmount() : 1;
|
2024-02-02 15:58:40 +01:00
|
|
|
if (toStack + amount <= maxStackable) {
|
|
|
|
toStack += amount;
|
|
|
|
remove.add(friendlyEntity);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break; //We max, exit loop
|
|
|
|
}
|
2019-06-28 22:00:30 +02:00
|
|
|
|
2024-02-02 15:58:40 +01:00
|
|
|
//Nothing to stack
|
|
|
|
if (toStack == 0) {
|
|
|
|
return;
|
|
|
|
}
|
2019-06-28 22:00:30 +02:00
|
|
|
|
2024-02-02 15:58:40 +01:00
|
|
|
//Add to base stack and remove stacked friends
|
|
|
|
stackManager.createStackedEntity(baseEntity, baseSize + toStack);
|
|
|
|
processed.add(baseEntity.getUniqueId());
|
2023-02-22 09:59:28 +01:00
|
|
|
|
2024-02-02 15:58:40 +01:00
|
|
|
//Remove merged entities
|
|
|
|
//We in sync, so we can remove entities
|
|
|
|
for (LivingEntity entity : remove) {
|
|
|
|
processed.add(entity.getUniqueId());
|
|
|
|
entity.remove();
|
2018-11-06 04:33:10 +01:00
|
|
|
}
|
2019-07-19 02:49:41 +02:00
|
|
|
}
|
|
|
|
|
2024-02-02 15:58:40 +01:00
|
|
|
/**
|
|
|
|
* This method splitting overstacked entities into new stacks.
|
|
|
|
* Must be called synchronously.
|
2024-03-12 20:25:22 +01:00
|
|
|
*
|
|
|
|
* @param baseStack The base stack to check for splitting.
|
2024-02-02 15:58:40 +01:00
|
|
|
* @param maxEntityStackSize The maximum stack size for the entity. -1 if we need to calculate it.
|
|
|
|
* @return True if the split was successful, false otherwise.
|
|
|
|
*/
|
|
|
|
public boolean attemptSplit(EntityStack baseStack, int maxEntityStackSize) {
|
|
|
|
LivingEntity hostEntity = baseStack.getHostEntity();
|
2023-02-04 14:48:13 +01:00
|
|
|
int stackSize = baseStack.getAmount();
|
2024-02-02 15:58:40 +01:00
|
|
|
int maxEntityStackAmount = maxEntityStackSize == -1 ? getEntityMaxStackSize(hostEntity) : maxEntityStackSize;
|
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
|
|
|
|
2023-02-04 14:48:13 +01:00
|
|
|
baseStack.setAmount(maxEntityStackAmount);
|
2019-07-17 14:56:50 +02:00
|
|
|
|
2024-02-02 15:58:40 +01:00
|
|
|
int finalStackSize = stackSize - maxEntityStackAmount;
|
|
|
|
do {
|
|
|
|
// Create a new stack, summon entity and add to stack.
|
|
|
|
LivingEntity newEntity = (LivingEntity) hostEntity.getWorld().spawnEntity(hostEntity.getLocation(), hostEntity.getType());
|
|
|
|
int toAdd = Math.min(finalStackSize, maxEntityStackAmount);
|
|
|
|
EntityStack newStack = stackManager.createStackedEntity(newEntity, toAdd);
|
|
|
|
processed.add(newEntity.getUniqueId());
|
|
|
|
finalStackSize -= maxEntityStackAmount;
|
|
|
|
} while (finalStackSize >= 0);
|
2019-07-19 02:50:07 +02:00
|
|
|
|
2023-02-22 09:59:28 +01:00
|
|
|
//Mark it as processed.
|
2024-02-02 15:58:40 +01:00
|
|
|
processed.add(hostEntity.getUniqueId());
|
2019-07-19 02:50:07 +02:00
|
|
|
return true;
|
2019-07-17 14:56:50 +02:00
|
|
|
}
|
|
|
|
|
2024-02-02 15:58:40 +01:00
|
|
|
private Set<CachedChunk> getNearbyChunks(SWorld sWorld, Location location) {
|
|
|
|
//Only stack entities in the same chunk
|
|
|
|
if (stackWholeChunk && chunkRadius == 0) {
|
|
|
|
return Collections.singleton(new CachedChunk(sWorld, location.getChunk().getX(), location.getChunk().getZ()));
|
2023-02-22 09:59:28 +01:00
|
|
|
}
|
2020-08-25 01:01:11 +02:00
|
|
|
World world = location.getWorld();
|
2024-02-02 15:58:40 +01:00
|
|
|
if (world == null) return new HashSet<>();
|
2020-08-25 01:01:11 +02:00
|
|
|
|
2021-06-13 17:40:46 +02:00
|
|
|
CachedChunk firstChunk = new CachedChunk(sWorld, location);
|
2024-02-02 15:58:40 +01:00
|
|
|
Set<CachedChunk> chunks = new TreeSet<>(Comparator.comparingInt(CachedChunk::getX).thenComparingInt(CachedChunk::getZ));
|
2020-08-25 01:01:11 +02:00
|
|
|
chunks.add(firstChunk);
|
|
|
|
|
2024-02-02 15:58:40 +01:00
|
|
|
//Calculate chunk coordinates we need to check
|
|
|
|
int minX = (int) Math.floor((location.getX() - chunkRadius) / 16.0D);
|
|
|
|
int maxX = (int) Math.floor((location.getX() + chunkRadius) / 16.0D);
|
|
|
|
int minZ = (int) Math.floor((location.getZ() - chunkRadius) / 16.0D);
|
|
|
|
int maxZ = (int) Math.floor((location.getZ() + chunkRadius) / 16.0D);
|
2020-08-25 01:01:11 +02:00
|
|
|
|
|
|
|
for (int x = minX; x <= maxX; ++x) {
|
|
|
|
for (int z = minZ; z <= maxZ; ++z) {
|
2024-02-02 15:58:40 +01:00
|
|
|
if (x == minX || x == maxX || z == minZ || z == maxZ) {
|
|
|
|
chunks.add(new CachedChunk(sWorld, x, z));
|
|
|
|
}
|
2020-08-25 01:01:11 +02:00
|
|
|
}
|
|
|
|
}
|
2024-02-02 15:58:40 +01:00
|
|
|
|
|
|
|
//Set a bedrock in the top left corner of the chunks
|
|
|
|
for (CachedChunk chunk : chunks) {
|
|
|
|
int x = chunk.getX() * 16;
|
|
|
|
int z = chunk.getZ() * 16;
|
|
|
|
world.getBlockAt(x, 319, z).setType(XMaterial.BEDROCK.parseMaterial());
|
|
|
|
}
|
2020-08-25 01:01:11 +02:00
|
|
|
return chunks;
|
|
|
|
}
|
|
|
|
|
2023-02-22 09:59:28 +01:00
|
|
|
/**
|
|
|
|
* Get all entities around an entity within a radius which are similar to the entity.
|
2024-03-12 20:25:22 +01:00
|
|
|
*
|
2023-02-22 09:59:28 +01:00
|
|
|
* @param entity The entity to get similar entities around.
|
|
|
|
* @return A list of similar entities around the entity.
|
|
|
|
*/
|
2024-02-02 15:58:40 +01:00
|
|
|
public List<LivingEntity> getFriendlyStacksNearby(LivingEntity entity) {
|
|
|
|
if (!stackWholeChunk) {
|
2024-03-12 20:25:22 +01:00
|
|
|
return entity.getNearbyEntities(searchRadius / 2.0, searchRadius / 2.0, searchRadius / 2.0)
|
2024-02-02 15:58:40 +01:00
|
|
|
.stream().filter(e -> e.getType() == entity.getType() && !isMaxStack((LivingEntity) e))
|
|
|
|
.map(e -> (LivingEntity) e).collect(Collectors.toList());
|
|
|
|
}
|
2020-08-25 01:01:11 +02:00
|
|
|
List<LivingEntity> entities = new ArrayList<>();
|
2023-03-29 21:21:00 +02:00
|
|
|
try {
|
2024-02-02 15:58:40 +01:00
|
|
|
Set<CachedChunk> chunks = getNearbyChunks(new SWorld(entity.getWorld()), entity.getLocation());
|
2023-03-29 21:21:00 +02:00
|
|
|
for (CachedChunk chunk : chunks) {
|
2023-04-07 21:44:08 +02:00
|
|
|
Entity[] entityList = chunk.getEntities();
|
2023-03-29 21:21:00 +02:00
|
|
|
for (Entity e : entityList) {
|
2024-02-02 15:58:40 +01:00
|
|
|
if (e.getType() == entity.getType() && !isMaxStack((LivingEntity) e)) {
|
2023-03-29 21:21:00 +02:00
|
|
|
entities.add((LivingEntity) e);
|
|
|
|
}
|
2020-09-07 16:49:08 +02:00
|
|
|
}
|
2020-08-25 01:01:11 +02:00
|
|
|
}
|
2023-03-29 21:21:00 +02:00
|
|
|
entities.removeIf(entity1 -> entity1.equals(entity) || !UltimateStacker.getInstance().getCustomEntityManager().isStackable(entity1));
|
|
|
|
} catch (Exception ex) {
|
|
|
|
ex.printStackTrace();
|
2020-08-25 01:01:11 +02:00
|
|
|
}
|
|
|
|
return entities;
|
|
|
|
}
|
|
|
|
|
2024-02-02 15:58:40 +01:00
|
|
|
public List<LivingEntity> getSimilarEntitiesAroundEntity(LivingEntity initialEntity, Location location) {
|
2023-04-07 21:44:08 +02:00
|
|
|
try {
|
|
|
|
// Create a list of all entities around the initial entity of the same type.
|
2024-02-02 15:58:40 +01:00
|
|
|
List<LivingEntity> entityList = new ArrayList<>(getFriendlyStacksNearby(initialEntity));
|
2023-04-07 21:44:08 +02:00
|
|
|
|
|
|
|
CustomEntity customEntity = plugin.getCustomEntityManager().getCustomEntity(initialEntity);
|
|
|
|
if (customEntity != null)
|
|
|
|
entityList.removeIf(entity -> !customEntity.isSimilar(initialEntity, entity));
|
|
|
|
|
|
|
|
if (stackFlyingDown && canFly(initialEntity))
|
|
|
|
entityList.removeIf(entity -> entity.getLocation().getY() > initialEntity.getLocation().getY());
|
|
|
|
|
|
|
|
for (Check check : checks) {
|
|
|
|
if (check == null) continue;
|
|
|
|
switch (check) {
|
|
|
|
case SPAWN_REASON: {
|
|
|
|
if (initialEntity.hasMetadata("US_REASON"))
|
|
|
|
entityList.removeIf(entity -> entity.hasMetadata("US_REASON") && !entity.getMetadata("US_REASON").get(0).asString().equals("US_REASON"));
|
2020-08-25 01:01:11 +02:00
|
|
|
}
|
2023-04-07 21:44:08 +02:00
|
|
|
case AGE: {
|
|
|
|
if (!(initialEntity instanceof Ageable)) break;
|
|
|
|
|
|
|
|
if (((Ageable) initialEntity).isAdult()) {
|
|
|
|
entityList.removeIf(entity -> !((Ageable) entity).isAdult());
|
|
|
|
} else {
|
|
|
|
entityList.removeIf(entity -> ((Ageable) entity).isAdult());
|
|
|
|
}
|
|
|
|
break;
|
2020-08-25 01:01:11 +02:00
|
|
|
}
|
2023-04-07 21:44:08 +02:00
|
|
|
case NERFED: {
|
|
|
|
if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_9)) break;
|
|
|
|
entityList.removeIf(entity -> entity.hasAI() != initialEntity.hasAI());
|
|
|
|
}
|
|
|
|
case IS_TAMED: {
|
|
|
|
if (!(initialEntity instanceof Tameable)) break;
|
|
|
|
if (((Tameable) initialEntity).isTamed()) {
|
|
|
|
entityList.removeIf(entity -> !((Tameable) entity).isTamed());
|
|
|
|
} else {
|
|
|
|
entityList.removeIf(entity -> ((Tameable) entity).isTamed());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case ANIMAL_OWNER: {
|
|
|
|
if (!(initialEntity instanceof Tameable)) break;
|
2020-08-25 01:01:11 +02:00
|
|
|
|
2023-04-07 21:44:08 +02:00
|
|
|
Tameable tameable = ((Tameable) initialEntity);
|
|
|
|
entityList.removeIf(entity -> ((Tameable) entity).getOwner() != tameable.getOwner());
|
|
|
|
}
|
|
|
|
case PIG_SADDLE: {
|
|
|
|
if (!(initialEntity instanceof Pig)) break;
|
|
|
|
entityList.removeIf(entity -> ((Pig) entity).hasSaddle());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SKELETON_TYPE: {
|
|
|
|
if (!(initialEntity instanceof Skeleton)) break;
|
2020-08-25 01:01:11 +02:00
|
|
|
|
2023-04-07 21:44:08 +02:00
|
|
|
Skeleton skeleton = (Skeleton) initialEntity;
|
|
|
|
entityList.removeIf(entity -> ((Skeleton) entity).getSkeletonType() != skeleton.getSkeletonType());
|
|
|
|
break;
|
2020-08-25 01:01:11 +02:00
|
|
|
}
|
2023-04-07 21:44:08 +02:00
|
|
|
case SHEEP_COLOR: {
|
|
|
|
if (!(initialEntity instanceof Sheep)) break;
|
2020-08-25 01:01:11 +02:00
|
|
|
|
2023-04-07 21:44:08 +02:00
|
|
|
Sheep sheep = ((Sheep) initialEntity);
|
|
|
|
entityList.removeIf(entity -> ((Sheep) entity).getColor() != sheep.getColor());
|
|
|
|
break;
|
2020-08-25 01:01:11 +02:00
|
|
|
}
|
2023-04-07 21:44:08 +02:00
|
|
|
case SHEEP_SHEARED: {
|
|
|
|
if (!(initialEntity instanceof Sheep)) break;
|
|
|
|
|
|
|
|
Sheep sheep = ((Sheep) initialEntity);
|
|
|
|
if (sheep.isSheared()) {
|
|
|
|
entityList.removeIf(entity -> !((Sheep) entity).isSheared());
|
|
|
|
} else {
|
|
|
|
entityList.removeIf(entity -> ((Sheep) entity).isSheared());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SNOWMAN_DERPED: {
|
|
|
|
if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_9)
|
|
|
|
|| !(initialEntity instanceof Snowman)) break;
|
|
|
|
|
|
|
|
Snowman snowman = ((Snowman) initialEntity);
|
|
|
|
if (snowman.isDerp()) {
|
|
|
|
entityList.removeIf(entity -> !((Snowman) entity).isDerp());
|
|
|
|
} else {
|
|
|
|
entityList.removeIf(entity -> ((Snowman) entity).isDerp());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LLAMA_COLOR: {
|
|
|
|
if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_11)
|
|
|
|
|| !(initialEntity instanceof Llama)) break;
|
|
|
|
Llama llama = ((Llama) initialEntity);
|
|
|
|
entityList.removeIf(entity -> ((Llama) entity).getColor() != llama.getColor());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LLAMA_STRENGTH: {
|
|
|
|
if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_11)
|
|
|
|
|| !(initialEntity instanceof Llama)) break;
|
|
|
|
Llama llama = ((Llama) initialEntity);
|
|
|
|
entityList.removeIf(entity -> ((Llama) entity).getStrength() != llama.getStrength());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case VILLAGER_PROFESSION: {
|
|
|
|
if (!(initialEntity instanceof Villager)) break;
|
|
|
|
Villager villager = ((Villager) initialEntity);
|
|
|
|
entityList.removeIf(entity -> ((Villager) entity).getProfession() != villager.getProfession());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SLIME_SIZE: {
|
|
|
|
if (!(initialEntity instanceof Slime)) break;
|
|
|
|
Slime slime = ((Slime) initialEntity);
|
|
|
|
entityList.removeIf(entity -> ((Slime) entity).getSize() != slime.getSize());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case HORSE_CARRYING_CHEST: {
|
|
|
|
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_11)) {
|
|
|
|
if (!(initialEntity instanceof ChestedHorse)) break;
|
|
|
|
entityList.removeIf(entity -> ((ChestedHorse) entity).isCarryingChest());
|
|
|
|
} else {
|
|
|
|
if (!(initialEntity instanceof Horse)) break;
|
|
|
|
entityList.removeIf(entity -> ((Horse) entity).isCarryingChest());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case HORSE_HAS_ARMOR: {
|
2020-08-25 01:01:11 +02:00
|
|
|
if (!(initialEntity instanceof Horse)) break;
|
2023-04-07 21:44:08 +02:00
|
|
|
entityList.removeIf(entity -> ((Horse) entity).getInventory().getArmor() != null);
|
|
|
|
break;
|
2020-08-25 01:01:11 +02:00
|
|
|
}
|
2023-04-07 21:44:08 +02:00
|
|
|
case HORSE_HAS_SADDLE: {
|
|
|
|
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)
|
|
|
|
&& initialEntity instanceof AbstractHorse) {
|
|
|
|
entityList.removeIf(entity -> ((AbstractHorse) entity).getInventory().getSaddle() != null);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!(initialEntity instanceof Horse)) break;
|
|
|
|
entityList.removeIf(entity -> ((Horse) entity).getInventory().getSaddle() != null);
|
2020-08-25 01:01:11 +02:00
|
|
|
break;
|
|
|
|
}
|
2023-04-07 21:44:08 +02:00
|
|
|
case HORSE_JUMP: {
|
|
|
|
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_11)) {
|
|
|
|
if (!(initialEntity instanceof AbstractHorse)) break;
|
|
|
|
AbstractHorse horse = ((AbstractHorse) initialEntity);
|
|
|
|
entityList.removeIf(entity -> ((AbstractHorse) entity).getJumpStrength() != horse.getJumpStrength());
|
|
|
|
} else {
|
|
|
|
if (!(initialEntity instanceof Horse)) break;
|
|
|
|
Horse horse = ((Horse) initialEntity);
|
|
|
|
entityList.removeIf(entity -> ((Horse) entity).getJumpStrength() != horse.getJumpStrength());
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case HORSE_COLOR: {
|
2020-08-25 01:01:11 +02:00
|
|
|
if (!(initialEntity instanceof Horse)) break;
|
|
|
|
Horse horse = ((Horse) initialEntity);
|
2023-04-07 21:44:08 +02:00
|
|
|
entityList.removeIf(entity -> ((Horse) entity).getColor() != horse.getColor());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case HORSE_STYLE: {
|
|
|
|
if (!(initialEntity instanceof Horse)) break;
|
|
|
|
Horse horse = ((Horse) initialEntity);
|
|
|
|
entityList.removeIf(entity -> ((Horse) entity).getStyle() != horse.getStyle());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ZOMBIE_BABY: {
|
|
|
|
if (!(initialEntity instanceof Zombie)) break;
|
|
|
|
Zombie zombie = (Zombie) initialEntity;
|
|
|
|
entityList.removeIf(entity -> ((Zombie) entity).isBaby() != zombie.isBaby());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case WOLF_COLLAR_COLOR: {
|
|
|
|
if (!(initialEntity instanceof Wolf)) break;
|
|
|
|
Wolf wolf = (Wolf) initialEntity;
|
|
|
|
entityList.removeIf(entity -> ((Wolf) entity).getCollarColor() != wolf.getCollarColor());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case OCELOT_TYPE: {
|
|
|
|
if (!(initialEntity instanceof Ocelot)) break;
|
|
|
|
Ocelot ocelot = (Ocelot) initialEntity;
|
|
|
|
entityList.removeIf(entity -> ((Ocelot) entity).getCatType() != ocelot.getCatType());
|
|
|
|
}
|
|
|
|
case CAT_TYPE: {
|
|
|
|
if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_14)
|
|
|
|
|| !(initialEntity instanceof Cat)) break;
|
|
|
|
Cat cat = (Cat) initialEntity;
|
|
|
|
entityList.removeIf(entity -> ((Cat) entity).getCatType() != cat.getCatType());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case HAS_EQUIPMENT: {
|
|
|
|
if (initialEntity.getEquipment() == null) break;
|
|
|
|
boolean imEquipped = isEquipped(initialEntity);
|
|
|
|
if (imEquipped)
|
|
|
|
entityList = new ArrayList<>();
|
|
|
|
else
|
|
|
|
entityList.removeIf(this::isEquipped);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case RABBIT_TYPE: {
|
|
|
|
if (!(initialEntity instanceof Rabbit)) break;
|
|
|
|
Rabbit rabbit = (Rabbit) initialEntity;
|
|
|
|
entityList.removeIf(entity -> ((Rabbit) entity).getRabbitType() != rabbit.getRabbitType());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case PARROT_TYPE: {
|
|
|
|
if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_12)
|
|
|
|
|| !(initialEntity instanceof Parrot)) break;
|
|
|
|
Parrot parrot = (Parrot) initialEntity;
|
|
|
|
entityList.removeIf(entity -> ((Parrot) entity).getVariant() != parrot.getVariant());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case PUFFERFISH_STATE: {
|
|
|
|
if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)
|
|
|
|
|| !(initialEntity instanceof PufferFish)) break;
|
|
|
|
PufferFish pufferFish = (PufferFish) initialEntity;
|
|
|
|
entityList.removeIf(entity -> ((PufferFish) entity).getPuffState() != pufferFish.getPuffState());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TROPICALFISH_PATTERN: {
|
|
|
|
if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)
|
|
|
|
|| !(initialEntity instanceof TropicalFish)) break;
|
|
|
|
TropicalFish tropicalFish = (TropicalFish) initialEntity;
|
|
|
|
entityList.removeIf(entity -> ((TropicalFish) entity).getPattern() != tropicalFish.getPattern());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TROPICALFISH_PATTERN_COLOR: {
|
|
|
|
if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)
|
|
|
|
|| !(initialEntity instanceof TropicalFish)) break;
|
|
|
|
TropicalFish tropicalFish = (TropicalFish) initialEntity;
|
|
|
|
entityList.removeIf(entity -> ((TropicalFish) entity).getPatternColor() != tropicalFish.getPatternColor());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TROPICALFISH_BODY_COLOR: {
|
|
|
|
if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)
|
|
|
|
|| !(initialEntity instanceof TropicalFish)) break;
|
|
|
|
TropicalFish tropicalFish = (TropicalFish) initialEntity;
|
|
|
|
entityList.removeIf(entity -> ((TropicalFish) entity).getBodyColor() != tropicalFish.getBodyColor());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case PHANTOM_SIZE: {
|
|
|
|
if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)
|
|
|
|
|| !(initialEntity instanceof Phantom)) break;
|
|
|
|
Phantom phantom = (Phantom) initialEntity;
|
|
|
|
entityList.removeIf(entity -> ((Phantom) entity).getSize() != phantom.getSize());
|
|
|
|
break;
|
2020-08-25 01:01:11 +02:00
|
|
|
}
|
2024-03-12 03:16:05 +01:00
|
|
|
case AXOLOTL_VARIANT: {
|
|
|
|
if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_17)
|
|
|
|
|| !(initialEntity instanceof Axolotl)) break;
|
|
|
|
Axolotl axolotl = (Axolotl) initialEntity;
|
|
|
|
entityList.removeIf(entity -> ((Axolotl) entity).getVariant() != axolotl.getVariant());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GOAT_HAS_HORNS: {
|
|
|
|
if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_17)
|
|
|
|
|| !(initialEntity instanceof Goat)) break;
|
|
|
|
Goat goat = (Goat) initialEntity;
|
|
|
|
boolean hasLeftHorn = goat.hasLeftHorn();
|
|
|
|
boolean hasRightHorn = goat.hasRightHorn();
|
|
|
|
entityList.removeIf(entity -> {
|
|
|
|
Goat otherGoat = (Goat) entity;
|
|
|
|
return otherGoat.hasLeftHorn() != hasLeftHorn || otherGoat.hasRightHorn() != hasRightHorn;
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case FROG_VARIANT: {
|
|
|
|
if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_19)
|
|
|
|
|| !(initialEntity instanceof Frog)) break;
|
|
|
|
Frog frog = (Frog) initialEntity;
|
|
|
|
entityList.removeIf(entity -> ((Frog) entity).getVariant() != frog.getVariant());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TADPOLE_AGE: {
|
|
|
|
if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_19)
|
|
|
|
|| !(initialEntity instanceof Tadpole)) break;
|
|
|
|
Tadpole tadpole = (Tadpole) initialEntity;
|
|
|
|
entityList.removeIf(entity -> ((Tadpole) entity).getAge() != tadpole.getAge());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case WARDEN_ANGER_LEVEL: {
|
|
|
|
if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_19)
|
|
|
|
|| !(initialEntity instanceof Warden)) break;
|
|
|
|
Warden warden = (Warden) initialEntity;
|
|
|
|
entityList.removeIf(entity -> ((Warden) entity).getAnger() != warden.getAnger());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case FOX_TYPE: {
|
|
|
|
if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_14)
|
|
|
|
|| !(initialEntity instanceof Fox)) break;
|
|
|
|
Fox fox = (Fox) initialEntity;
|
|
|
|
entityList.removeIf(entity -> ((Fox) entity).getFoxType() != fox.getFoxType());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case HOGLIN_IMMUNE: {
|
|
|
|
if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_16)
|
|
|
|
|| !(initialEntity instanceof Hoglin)) break;
|
|
|
|
Hoglin hoglin = (Hoglin) initialEntity;
|
|
|
|
if (hoglin.isImmuneToZombification()) {
|
|
|
|
entityList.removeIf(entity -> !((Hoglin) entity).isImmuneToZombification());
|
|
|
|
} else {
|
|
|
|
entityList.removeIf(entity -> ((Hoglin) entity).isImmuneToZombification());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2020-08-25 01:01:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-07 21:44:08 +02:00
|
|
|
if (initialEntity.hasMetadata("breedCooldown")) {
|
|
|
|
entityList.removeIf(entity -> !entity.hasMetadata("breedCooldown"));
|
|
|
|
}
|
|
|
|
return entityList;
|
|
|
|
} catch (Exception ex) {
|
|
|
|
ex.printStackTrace();
|
2020-08-25 01:01:11 +02:00
|
|
|
}
|
2023-04-07 21:44:08 +02:00
|
|
|
return new ArrayList<>();
|
2020-08-25 01:01:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isEquipped(LivingEntity initialEntity) {
|
|
|
|
if (initialEntity.getEquipment() == null) return false;
|
|
|
|
EntityEquipment equipment = initialEntity.getEquipment();
|
2019-07-19 02:49:41 +02:00
|
|
|
|
2020-08-25 01:01:11 +02:00
|
|
|
return (equipment.getItemInHand().getType() != Material.AIR
|
|
|
|
&& !weaponsArentEquipment && !equipment.getItemInHand().getEnchantments().isEmpty()
|
|
|
|
|| (equipment.getHelmet() != null && equipment.getHelmet().getType() != Material.AIR)
|
|
|
|
|| (equipment.getChestplate() != null && equipment.getChestplate().getType() != Material.AIR)
|
|
|
|
|| (equipment.getLeggings() != null && equipment.getLeggings().getType() != Material.AIR)
|
|
|
|
|| (equipment.getBoots() != null && equipment.getBoots().getType() != Material.AIR));
|
2019-06-28 22:00:30 +02:00
|
|
|
}
|
|
|
|
|
2024-02-02 15:58:40 +01:00
|
|
|
private int getEntityMaxStackSize(LivingEntity initialEntity) {
|
|
|
|
return entityStackSizes.computeIfAbsent(initialEntity.getType(), type -> {
|
|
|
|
int maxStackSize = configurationSection.getInt("Mobs." + initialEntity.getType().name() + ".Max Stack Size");
|
|
|
|
if (maxStackSize == -1) {
|
|
|
|
maxStackSize = maxEntityStackSize;
|
2019-09-10 22:45:26 +02:00
|
|
|
}
|
2024-02-02 15:58:40 +01:00
|
|
|
return maxStackSize;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean isMaxStack(LivingEntity livingEntity) {
|
|
|
|
EntityStack stack = stackManager.getStackedEntity(livingEntity);
|
|
|
|
if (stack == null) return false;
|
|
|
|
return stack.getAmount() >= getEntityMaxStackSize(livingEntity);
|
2019-06-30 01:32:16 +02:00
|
|
|
}
|
2020-08-25 01:01:11 +02:00
|
|
|
|
|
|
|
public boolean canFly(LivingEntity entity) {
|
|
|
|
switch (entity.getType()) {
|
|
|
|
case GHAST:
|
|
|
|
case BLAZE:
|
|
|
|
case PHANTOM:
|
|
|
|
case BAT:
|
|
|
|
case BEE:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2018-11-06 04:33:10 +01:00
|
|
|
}
|