Implement tree capitator
This commit is contained in:
parent
4a602b42f6
commit
0d37534eed
@ -15,4 +15,8 @@ commands:
|
||||
search:
|
||||
description: Search for a given item in all nearby inventories
|
||||
usage: /<command> {item type}
|
||||
permission: invtweaks.search
|
||||
permission: invtweaks.search
|
||||
capitator:
|
||||
description: Toggle tree capitation for an axe in your main hand
|
||||
usage: /<command>
|
||||
permission: invtweaks.capitator
|
@ -1,22 +1,25 @@
|
||||
package dev.w1zzrd.invtweaks;
|
||||
|
||||
import dev.w1zzrd.invtweaks.command.CapitatorCommand;
|
||||
import dev.w1zzrd.invtweaks.command.MagnetCommandExecutor;
|
||||
import dev.w1zzrd.invtweaks.command.SearchCommandExecutor;
|
||||
import dev.w1zzrd.invtweaks.command.SortCommandExecutor;
|
||||
import dev.w1zzrd.invtweaks.listener.MagnetismListener;
|
||||
import dev.w1zzrd.invtweaks.listener.TabCompletionListener;
|
||||
import dev.w1zzrd.invtweaks.enchantment.CapitatorEnchantment;
|
||||
import dev.w1zzrd.invtweaks.listener.*;
|
||||
import dev.w1zzrd.invtweaks.serialization.MagnetConfig;
|
||||
import dev.w1zzrd.invtweaks.listener.SortListener;
|
||||
import dev.w1zzrd.invtweaks.listener.StackReplaceListener;
|
||||
import dev.w1zzrd.invtweaks.serialization.MagnetData;
|
||||
import dev.w1zzrd.invtweaks.serialization.SearchConfig;
|
||||
import dev.w1zzrd.invtweaks.serialization.UUIDList;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerialization;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@ -31,18 +34,24 @@ public final class InvTweaksPlugin extends JavaPlugin {
|
||||
public static final String LOG_PLUGIN_NAME = "[InventoryTweaks]";
|
||||
private static final String PERSISTENT_DATA_NAME = "data";
|
||||
|
||||
private static final String ENCHANTMENT_CAPITATOR_NAME = "Capitator";
|
||||
|
||||
private final Logger logger = Bukkit.getLogger();
|
||||
|
||||
// Command executor references in case I need them or something idk
|
||||
private SortCommandExecutor sortCommandExecutor;
|
||||
private MagnetCommandExecutor magnetCommandExecutor;
|
||||
private SearchCommandExecutor searchCommandExecutor;
|
||||
private CapitatorCommand capitatorCommand;
|
||||
private DataStore data;
|
||||
private NamespacedKey capitatorEnchantmentKey;
|
||||
private Enchantment capitatorEnchantment;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
logger.fine(LOG_PLUGIN_NAME + " Plugin enabled");
|
||||
|
||||
initEnchantments();
|
||||
enablePersistentData();
|
||||
initCommands();
|
||||
initEvents();
|
||||
@ -55,6 +64,7 @@ public final class InvTweaksPlugin extends JavaPlugin {
|
||||
disableEvents();
|
||||
disableCommands();
|
||||
disablePersistentData();
|
||||
disableEnchantments();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -78,18 +88,43 @@ public final class InvTweaksPlugin extends JavaPlugin {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize commands registered by this plugin
|
||||
*/
|
||||
private void initCommands() {
|
||||
sortCommandExecutor = new SortCommandExecutor();
|
||||
magnetCommandExecutor = new MagnetCommandExecutor(this, "magnet", getPersistentData());
|
||||
searchCommandExecutor = new SearchCommandExecutor(this, "search");
|
||||
private void initEnchantments() {
|
||||
capitatorEnchantmentKey = new NamespacedKey(this, ENCHANTMENT_CAPITATOR_NAME);
|
||||
capitatorEnchantment = new CapitatorEnchantment(ENCHANTMENT_CAPITATOR_NAME, capitatorEnchantmentKey);
|
||||
|
||||
// TODO: Bind command by annotation
|
||||
Objects.requireNonNull(getCommand("sort")).setExecutor(sortCommandExecutor);
|
||||
Objects.requireNonNull(getCommand("magnet")).setExecutor(magnetCommandExecutor);
|
||||
Objects.requireNonNull(getCommand("search")).setExecutor(searchCommandExecutor);
|
||||
try {
|
||||
final Field acceptingField = Enchantment.class.getDeclaredField("acceptingNew");
|
||||
acceptingField.setAccessible(true);
|
||||
|
||||
acceptingField.set(null, true);
|
||||
|
||||
Enchantment.registerEnchantment(capitatorEnchantment);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void disableEnchantments() {
|
||||
try {
|
||||
final Field byKeyField = Enchantment.class.getDeclaredField("byKey");
|
||||
final Field byNameField = Enchantment.class.getDeclaredField("byName");
|
||||
|
||||
byKeyField.setAccessible(true);
|
||||
byNameField.setAccessible(true);
|
||||
|
||||
final Object byKey = byKeyField.get(null);
|
||||
final Object byName = byNameField.get(null);
|
||||
|
||||
if (byKey instanceof final Map<?, ?> byKeyMap && byName instanceof final Map<?, ?> byNameMap) {
|
||||
byKeyMap.remove(capitatorEnchantmentKey);
|
||||
byNameMap.remove(ENCHANTMENT_CAPITATOR_NAME);
|
||||
}
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
capitatorEnchantment = null;
|
||||
capitatorEnchantmentKey = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -102,17 +137,7 @@ public final class InvTweaksPlugin extends JavaPlugin {
|
||||
pluginManager.registerEvents(new SortListener(), this);
|
||||
pluginManager.registerEvents(new MagnetismListener(magnetCommandExecutor), this);
|
||||
pluginManager.registerEvents(new TabCompletionListener(), this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do whatever is necessary to disable commands and their execution
|
||||
*/
|
||||
private void disableCommands() {
|
||||
magnetCommandExecutor.onDisable();
|
||||
|
||||
sortCommandExecutor = null;
|
||||
magnetCommandExecutor = null;
|
||||
searchCommandExecutor = null;
|
||||
pluginManager.registerEvents(new TreeCapitatorListener(capitatorEnchantment), this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -123,6 +148,34 @@ public final class InvTweaksPlugin extends JavaPlugin {
|
||||
HandlerList.unregisterAll(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize commands registered by this plugin
|
||||
*/
|
||||
private void initCommands() {
|
||||
sortCommandExecutor = new SortCommandExecutor();
|
||||
magnetCommandExecutor = new MagnetCommandExecutor(this, "magnet", getPersistentData());
|
||||
searchCommandExecutor = new SearchCommandExecutor(this, "search");
|
||||
capitatorCommand = new CapitatorCommand(capitatorEnchantment);
|
||||
|
||||
// TODO: Bind command by annotation
|
||||
Objects.requireNonNull(getCommand("sort")).setExecutor(sortCommandExecutor);
|
||||
Objects.requireNonNull(getCommand("magnet")).setExecutor(magnetCommandExecutor);
|
||||
Objects.requireNonNull(getCommand("search")).setExecutor(searchCommandExecutor);
|
||||
Objects.requireNonNull(getCommand("capitator")).setExecutor(capitatorCommand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do whatever is necessary to disable commands and their execution
|
||||
*/
|
||||
private void disableCommands() {
|
||||
magnetCommandExecutor.onDisable();
|
||||
|
||||
capitatorCommand = null;
|
||||
searchCommandExecutor = null;
|
||||
magnetCommandExecutor = null;
|
||||
sortCommandExecutor = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register type serializers/deserializers for configurations and YAML files
|
||||
*
|
||||
|
37
src/dev/w1zzrd/invtweaks/command/CapitatorCommand.java
Normal file
37
src/dev/w1zzrd/invtweaks/command/CapitatorCommand.java
Normal file
@ -0,0 +1,37 @@
|
||||
package dev.w1zzrd.invtweaks.command;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class CapitatorCommand implements CommandExecutor {
|
||||
private final Enchantment capitatorEnchantment;
|
||||
|
||||
public CapitatorCommand(final Enchantment capitatorEnchantment) {
|
||||
this.capitatorEnchantment = capitatorEnchantment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (sender instanceof final Player caller) {
|
||||
final ItemStack stack = caller.getInventory().getItemInMainHand();
|
||||
|
||||
if (stack.getType().name().endsWith("_AXE")) {
|
||||
if (stack.containsEnchantment(capitatorEnchantment)) {
|
||||
stack.removeEnchantment(capitatorEnchantment);
|
||||
sender.spigot().sendMessage(CommandUtils.successMessage("Item is now a regular axe"));
|
||||
} else {
|
||||
stack.addEnchantment(capitatorEnchantment, 1);
|
||||
sender.spigot().sendMessage(CommandUtils.successMessage("Item is now a capitator axe"));
|
||||
}
|
||||
} else
|
||||
sender.spigot().sendMessage(CommandUtils.errorMessage("Only axes can be tree capitators!"));
|
||||
} else
|
||||
sender.spigot().sendMessage(CommandUtils.errorMessage("Only players can create tree capitators!"));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package dev.w1zzrd.invtweaks.command;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
@ -16,4 +17,16 @@ public final class CommandUtils {
|
||||
|
||||
return !condition;
|
||||
}
|
||||
|
||||
public static BaseComponent errorMessage(final String message) {
|
||||
final BaseComponent component = new TextComponent(message);
|
||||
component.setColor(ChatColor.DARK_RED);
|
||||
return component;
|
||||
}
|
||||
|
||||
public static BaseComponent successMessage(final String message) {
|
||||
final BaseComponent component = new TextComponent(message);
|
||||
component.setColor(ChatColor.GREEN);
|
||||
return component;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
package dev.w1zzrd.invtweaks.enchantment;
|
||||
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.enchantments.EnchantmentTarget;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public final class CapitatorEnchantment extends Enchantment {
|
||||
private final String name;
|
||||
|
||||
public CapitatorEnchantment(String name, NamespacedKey key) {
|
||||
super(key);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxLevel() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStartLevel() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnchantmentTarget getItemTarget() {
|
||||
return EnchantmentTarget.TOOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTreasure() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCursed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean conflictsWith(Enchantment other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEnchantItem(ItemStack item) {
|
||||
return item.getType().name().endsWith("_AXE");
|
||||
}
|
||||
}
|
211
src/dev/w1zzrd/invtweaks/listener/TreeCapitatorListener.java
Normal file
211
src/dev/w1zzrd/invtweaks/listener/TreeCapitatorListener.java
Normal file
@ -0,0 +1,211 @@
|
||||
package dev.w1zzrd.invtweaks.listener;
|
||||
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.Damageable;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.bukkit.Material.*;
|
||||
|
||||
public class TreeCapitatorListener implements Listener {
|
||||
|
||||
private static final int MAX_SEARCH_BLOCKS = 69420;
|
||||
private static final List<Material> targetMaterials = Arrays.asList(
|
||||
ACACIA_LOG,
|
||||
OAK_LOG,
|
||||
BIRCH_LOG,
|
||||
JUNGLE_LOG,
|
||||
DARK_OAK_LOG,
|
||||
SPRUCE_LOG,
|
||||
|
||||
ACACIA_LEAVES,
|
||||
OAK_LEAVES,
|
||||
BIRCH_LEAVES,
|
||||
JUNGLE_LEAVES,
|
||||
DARK_OAK_LEAVES,
|
||||
SPRUCE_LEAVES
|
||||
);
|
||||
|
||||
private static final List<Material> leaves = Arrays.asList(
|
||||
ACACIA_LEAVES,
|
||||
OAK_LEAVES,
|
||||
BIRCH_LEAVES,
|
||||
JUNGLE_LEAVES,
|
||||
DARK_OAK_LEAVES,
|
||||
SPRUCE_LEAVES
|
||||
);
|
||||
|
||||
private final Enchantment capitatorEnchantment;
|
||||
|
||||
public TreeCapitatorListener(final Enchantment capitatorEnchantment) {
|
||||
this.capitatorEnchantment = capitatorEnchantment;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockBreak(final BlockBreakEvent event) {
|
||||
final ItemStack handTool = event.getPlayer().getInventory().getItemInMainHand();
|
||||
if (event.isCancelled() || !handTool.containsEnchantment(capitatorEnchantment))
|
||||
return;
|
||||
|
||||
if (handTool.getItemMeta() instanceof final Damageable tool) {
|
||||
if (!leaves.contains(event.getBlock().getType()) && targetMaterials.contains(event.getBlock().getType())) {
|
||||
int logBreakCount = 0;
|
||||
|
||||
for (final Block found : findAdjacent(event.getBlock(), getMaxUses(handTool, tool)))
|
||||
if (event.getPlayer().getGameMode() == GameMode.CREATIVE)
|
||||
found.setType(AIR);
|
||||
else {
|
||||
if (!leaves.contains(found.getType()))
|
||||
++logBreakCount;
|
||||
|
||||
found.breakNaturally(handTool);
|
||||
}
|
||||
|
||||
if (event.getPlayer().getGameMode() != GameMode.CREATIVE && !applyDamage(handTool, logBreakCount)) {
|
||||
event.getPlayer().getInventory().setItemInMainHand(new ItemStack(AIR));
|
||||
event.getPlayer().playSound(event.getPlayer().getLocation(), Sound.ENTITY_ITEM_BREAK, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getMaxUses(final ItemStack stack, final Damageable tool) {
|
||||
return ((stack.getEnchantmentLevel(Enchantment.DURABILITY) + 1) * (stack.getType().getMaxDurability()) - tool.getDamage());
|
||||
}
|
||||
|
||||
private static boolean applyDamage(final ItemStack stack, final int brokenBlocks) {
|
||||
final ItemMeta meta = stack.getItemMeta();
|
||||
|
||||
if (meta instanceof final Damageable toolMeta) {
|
||||
|
||||
final int unbreakingDivider = stack.getEnchantmentLevel(Enchantment.DURABILITY) + 1;
|
||||
final int round = brokenBlocks % unbreakingDivider;
|
||||
final int dmg = (brokenBlocks / unbreakingDivider) + (round != 0 ? 1 : 0);
|
||||
|
||||
if (dmg > (stack.getType().getMaxDurability() - toolMeta.getDamage()))
|
||||
return false;
|
||||
|
||||
toolMeta.setDamage(toolMeta.getDamage() + dmg);
|
||||
|
||||
stack.setItemMeta(meta);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static List<Block> findAdjacent(final Block start, final int softMax) {
|
||||
List<Block> frontier = new ArrayList<>();
|
||||
final List<Block> matches = new ArrayList<>();
|
||||
|
||||
frontier.add(start);
|
||||
matches.add(start);
|
||||
|
||||
int total = 1;
|
||||
int softMaxCount = 1;
|
||||
|
||||
// Keep finding blocks until we have no new matches
|
||||
while (frontier.size() > 0 && total < MAX_SEARCH_BLOCKS && softMaxCount < softMax) {
|
||||
final long result = addAdjacents(frontier, matches, total, softMaxCount, softMax);
|
||||
total = (int) (result >>> 32);
|
||||
softMaxCount = (int) (result & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
private static long addAdjacents(final List<Block> frontier, final List<Block> collect, int total, int softMax, final int softMaxCap) {
|
||||
final List<Block> newFrontier = new ArrayList<>();
|
||||
|
||||
OUTER:
|
||||
for (final Block check : frontier)
|
||||
for (int x = -1; x <= 1; ++x)
|
||||
for (int y = -1; y <= 1; ++y)
|
||||
for (int z = -1; z <= 1; ++z)
|
||||
if ((x | y | z) != 0) {
|
||||
final Block offset = offset(collect, check, x, y, z);
|
||||
|
||||
if (offset != null && !binaryContains(newFrontier, offset)) {
|
||||
binaryInsert(collect, offset);
|
||||
binaryInsert(newFrontier, offset);
|
||||
|
||||
if (!leaves.contains(offset.getType())) {
|
||||
++softMax;
|
||||
|
||||
if (softMax >= softMaxCap)
|
||||
break OUTER;
|
||||
}
|
||||
}
|
||||
|
||||
// Short-circuit for max search
|
||||
++total;
|
||||
if (total == MAX_SEARCH_BLOCKS)
|
||||
break OUTER;
|
||||
}
|
||||
|
||||
frontier.clear();
|
||||
frontier.addAll(newFrontier);
|
||||
|
||||
return (((long)total) << 32) | (((long) softMax) & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
private static Block offset(final List<Block> checked, final Block source, int x, int y, int z) {
|
||||
final Block offset = source.getWorld().getBlockAt(source.getLocation().add(x, y, z));
|
||||
|
||||
if (targetMaterials.contains(offset.getType()) && (!leaves.contains(source.getType()) || !leaves.contains(offset.getType())) && !binaryContains(checked, offset))
|
||||
return offset;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean binaryContains(final List<Block> collection, final Block find) {
|
||||
return binarySearchBlock(collection, find) >= 0;
|
||||
}
|
||||
|
||||
private static void binaryInsert(final List<Block> collection, final Block insert) {
|
||||
final int index = binarySearchBlock(collection, insert);
|
||||
|
||||
if (index >= 0)
|
||||
return;
|
||||
|
||||
collection.add(-(index + 1), insert);
|
||||
}
|
||||
|
||||
private static void binaryRemove(final List<Block> collection, final Block remove) {
|
||||
final int index = binarySearchBlock(collection, remove);
|
||||
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
collection.remove(index);
|
||||
}
|
||||
|
||||
private static int binarySearchBlock(final List<Block> collection, final Block find) {
|
||||
return Collections.binarySearch(collection, find, TreeCapitatorListener::blockCompare);
|
||||
}
|
||||
|
||||
private static int blockCompare(final Block b1, final Block b2) {
|
||||
return coordinateCompare(b1.getX(), b1.getY(), b1.getZ(), b2.getX(), b2.getY(), b2.getZ());
|
||||
}
|
||||
private static int coordinateCompare(final int x1, final int y1, final int z1, final int x2, final int y2, final int z2) {
|
||||
final int x = Integer.compare(x1, x2);
|
||||
if (x != 0)
|
||||
return x;
|
||||
|
||||
final int y = Integer.compare(y1, y2);
|
||||
if (y != 0)
|
||||
return y;
|
||||
|
||||
return Integer.compare(z1, z2);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user