diff --git a/ci-pom.xml b/ci-pom.xml index 810f033..d497512 100644 --- a/ci-pom.xml +++ b/ci-pom.xml @@ -7,7 +7,7 @@ us.mytheria BlobLib - 1.697.67 + 1.698.02 pom.xml bloblib diff --git a/local-pom.xml b/local-pom.xml index acbfdc3..72fb727 100644 --- a/local-pom.xml +++ b/local-pom.xml @@ -5,7 +5,7 @@ us.mytheria BlobLib - 1.697.69 + 1.698.02 pom.xml bloblib diff --git a/pom.xml b/pom.xml index b263140..566fb30 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 us.mytheria BlobLib - 1.697.69 + 1.698.02 pom diff --git a/src/main/java/us/mytheria/bloblib/api/BlobLibInventoryAPI.java b/src/main/java/us/mytheria/bloblib/api/BlobLibInventoryAPI.java index 54d23b8..ad5037a 100644 --- a/src/main/java/us/mytheria/bloblib/api/BlobLibInventoryAPI.java +++ b/src/main/java/us/mytheria/bloblib/api/BlobLibInventoryAPI.java @@ -419,11 +419,13 @@ public BlobSelector customSelector(@NotNull String blobInventoryKey, dataType, selectorList.get(), onReturn); selector.setItemsPerPage(selector.getSlots(buttonRangeKey) == null ? 1 : selector.getSlots(buttonRangeKey).size()); + selector.setWhiteBackgroundName(buttonRangeKey); if (display != null) selector.selectElement(player, onSelect, null, - display); + display, + selectorList::get); else selector.selectElement(player, onSelect, @@ -556,7 +558,8 @@ public BlobEditor customEditor(@NotNull String blobInventoryKey, playerSelector.selectElement(player, onAdd, null, - addDisplay); + addDisplay, + viewCollection); }, viewCollection.get(), onReturn)); BlobEditor editor = uber.thanks(); editor.setItemsPerPage(editor.getSlots(buttonRangeKey) == null diff --git a/src/main/java/us/mytheria/bloblib/displayentity/BlockDisplayPackFloatingPet.java b/src/main/java/us/mytheria/bloblib/displayentity/BlockDisplayPackFloatingPet.java new file mode 100644 index 0000000..2f21b80 --- /dev/null +++ b/src/main/java/us/mytheria/bloblib/displayentity/BlockDisplayPackFloatingPet.java @@ -0,0 +1,48 @@ +package us.mytheria.bloblib.displayentity; + +import org.bukkit.Location; +import org.bukkit.Particle; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.BlockDisplay; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import us.mytheria.bloblib.BlobLib; + +public class BlockDisplayPackFloatingPet extends DisplayPackFloatingPet { + /** + * Creates a pet + * + * @param owner - the FloatingPet owner + * @param display - the display (like BlockData/ItemStack) + * (must be an item or a block) + * @param particle - the Particle to itemStack + * @param customName - the CustomName of the pet + * (if null will be used 'owner's Pet') + */ + public BlockDisplayPackFloatingPet(Player owner, + BlockData display, + @Nullable Particle particle, + @Nullable String customName, + DisplayFloatingPetSettings settings, + @NotNull PackMaster packMaster, + int index) { + super(owner, display, particle, customName, settings, packMaster, index); + } + + void spawnEntity(Location location) { + BlobLib plugin = BlobLib.getInstance(); + entity = (BlockDisplay) location.getWorld().spawnEntity(location, + EntityType.BLOCK_DISPLAY); + entity.setPersistent(false); + entity.setTeleportDuration(1); + setCustomName(getCustomName()); + entity.setBlock(getDisplay()); + initAnimations(plugin); + } + + public void setDisplay(BlockData display) { + this.display = display; + } +} diff --git a/src/main/java/us/mytheria/bloblib/displayentity/DisplayFloatingPet.java b/src/main/java/us/mytheria/bloblib/displayentity/DisplayFloatingPet.java index 9425450..e39bded 100644 --- a/src/main/java/us/mytheria/bloblib/displayentity/DisplayFloatingPet.java +++ b/src/main/java/us/mytheria/bloblib/displayentity/DisplayFloatingPet.java @@ -31,10 +31,10 @@ public abstract class DisplayFloatingPet private UUID owner; private boolean activated, pauseLogic; private String customName; - private SyncDisplayEntityAnimations animations; - private BukkitTask logicTask; + protected SyncDisplayEntityAnimations animations; + protected BukkitTask logicTask; protected R display; - private final DisplayFloatingPetSettings settings; + protected final DisplayFloatingPetSettings settings; /** * Creates a pet @@ -45,6 +45,7 @@ public abstract class DisplayFloatingPet * @param particle - the Particle to itemStack * @param customName - the CustomName of the pet * (if null will be used 'owner's Pet') + * @param settings - the settings of the pet */ public DisplayFloatingPet(@NotNull Player owner, @NotNull R display, @@ -110,7 +111,7 @@ protected void initAnimations(JavaPlugin plugin) { initLogic(plugin); } - private void initLogic(JavaPlugin plugin) { + protected void initLogic(JavaPlugin plugin) { EntityAnimationsCarrier animationsCarrier = settings.animationsCarrier(); logicTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> { Player owner = findOwner(); @@ -140,11 +141,11 @@ private void moveAway() { this.location = animations.moveAway(findOwnerOrFail(), location); } - private void move() { + protected void move() { this.location = animations.move(findOwnerOrFail(), location); } - private void idle() { + protected void idle() { this.location = animations.idle(findOwnerOrFail(), location); } @@ -265,7 +266,7 @@ public Location getLocation() { return location; } - private void setLocation(Location location) { + protected void setLocation(Location location) { this.location = location; } @@ -281,7 +282,7 @@ public boolean isActive() { return activated; } - private void setActive(boolean activated) { + protected void setActive(boolean activated) { this.activated = activated; } diff --git a/src/main/java/us/mytheria/bloblib/displayentity/DisplayPackFloatingPet.java b/src/main/java/us/mytheria/bloblib/displayentity/DisplayPackFloatingPet.java new file mode 100644 index 0000000..686fe79 --- /dev/null +++ b/src/main/java/us/mytheria/bloblib/displayentity/DisplayPackFloatingPet.java @@ -0,0 +1,79 @@ +package us.mytheria.bloblib.displayentity; + +import org.bukkit.Bukkit; +import org.bukkit.Particle; +import org.bukkit.entity.Display; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a FloatingPet. + * Uses 1.20.2+ Bukkit API + * + * @param - the Display entity + * @param - (BlockData/ItemStack) + */ +public abstract class DisplayPackFloatingPet + extends DisplayFloatingPet { + protected final PackMaster> packMaster; + private final int index, holdIndex; + + /** + * Creates a pet + * + * @param owner - the FloatingPet owner + * @param display - the display (like BlockData/ItemStack) + * (must be an item or a block) + * @param particle - the Particle to itemStack + * @param customName - the CustomName of the pet + * (if null will be used 'owner's Pet') + * @param settings - the settings of the pet + */ + public DisplayPackFloatingPet(@NotNull Player owner, + @NotNull R display, + @Nullable Particle particle, + @Nullable String customName, + @NotNull DisplayFloatingPetSettings settings, + @NotNull PackMaster> packMaster, + int index) { + super(owner, display, particle, customName, settings); + this.packMaster = packMaster; + this.index = index; + this.holdIndex = packMaster.addComponent(this, index); + } + + @Override + protected void initLogic(JavaPlugin plugin) { + EntityAnimationsCarrier animationsCarrier = settings.animationsCarrier(); + logicTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> { + Player owner = findOwner(); + if (owner == null || !owner.isOnline()) { + destroy(); + return; + } + spawnParticles(animationsCarrier.particlesOffset(), 0); + if (!isPauseLogic()) + move(); + }, 0, 1); + } + + @Override + public void destroy() { + super.destroy(); + } + + @Override + protected void initAnimations(JavaPlugin plugin) { + animations = new SyncDisplayPackEntityAnimations<>(this, + settings.animationsCarrier(), + packMaster, + holdIndex); + initLogic(plugin); + } + + public int getIndex() { + return index; + } +} \ No newline at end of file diff --git a/src/main/java/us/mytheria/bloblib/displayentity/ItemDisplayPackFloatingPet.java b/src/main/java/us/mytheria/bloblib/displayentity/ItemDisplayPackFloatingPet.java new file mode 100644 index 0000000..4fbb08b --- /dev/null +++ b/src/main/java/us/mytheria/bloblib/displayentity/ItemDisplayPackFloatingPet.java @@ -0,0 +1,58 @@ +package us.mytheria.bloblib.displayentity; + +import org.bukkit.Location; +import org.bukkit.Particle; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.ItemDisplay; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Transformation; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import us.mytheria.bloblib.BlobLib; +import us.mytheria.bloblib.entities.display.DisplayDecorator; + +public class ItemDisplayPackFloatingPet extends DisplayPackFloatingPet { + /** + * Creates a pet + * + * @param owner - the FloatingPet owner + * @param display - the display (like BlockData/ItemStack) + * (must be an item or a block) + * @param particle - the Particle to itemStack + * @param customName - the CustomName of the pet + * (if null will be used 'owner's Pet') + */ + public ItemDisplayPackFloatingPet(Player owner, + ItemStack display, + @Nullable Particle particle, + @Nullable String customName, + DisplayFloatingPetSettings settings, + @NotNull PackMaster packMaster, + int index) { + super(owner, display, particle, customName, settings, packMaster, index); + } + + void spawnEntity(Location location) { + BlobLib plugin = BlobLib.getInstance(); + entity = (ItemDisplay) location.getWorld().spawnEntity(location, + EntityType.ITEM_DISPLAY); + entity.setItemDisplayTransform(ItemDisplay.ItemDisplayTransform.FIXED); + entity.setPersistent(false); + entity.setTeleportDuration(1); + setCustomName(getCustomName()); + entity.setItemStack(getDisplay()); + Transformation transformation = entity.getTransformation(); + entity.setTransformation(new Transformation( + transformation.getTranslation().add(0f, -0.5f, 0f), + transformation.getLeftRotation(), transformation.getScale(), + transformation.getRightRotation())); + DisplayDecorator decorator = new DisplayDecorator<>(entity, plugin); + decorator.transformLeft(1, 0, 0, 90, 1); + initAnimations(plugin); + } + + public void setDisplay(ItemStack display) { + this.display = display; + } +} diff --git a/src/main/java/us/mytheria/bloblib/displayentity/PackMaster.java b/src/main/java/us/mytheria/bloblib/displayentity/PackMaster.java new file mode 100644 index 0000000..3eaf5d1 --- /dev/null +++ b/src/main/java/us/mytheria/bloblib/displayentity/PackMaster.java @@ -0,0 +1,54 @@ +package us.mytheria.bloblib.displayentity; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; +import org.joml.Vector2d; +import us.mytheria.bloblib.entities.SquareMaster; +import us.mytheria.bloblib.gists.RelativeLocation; + +import java.util.Map; +import java.util.Objects; + +public class PackMaster extends SquareMaster { + private @NotNull Vector pivot; + + public static PackMaster of(int maxPerRow, + double componentLength, + @NotNull Map components, + @NotNull Vector pivot) { + Objects.requireNonNull(components, "'components' cannot be null!"); + Objects.requireNonNull(pivot, "'pivot' cannot be null!"); + return new PackMaster<>(maxPerRow, componentLength, components, pivot); + } + + private PackMaster(int maxPerRow, + double componentLength, + @NotNull Map components, + @NotNull Vector pivot) { + super(maxPerRow, componentLength, components); + this.pivot = pivot; + } + + @NotNull + public Vector getPivot() { + return pivot; + } + + public Location pivot(Player player, int index) { + Location location = player.getLocation().clone(); + location.setPitch(Location.normalizePitch(0)); + Vector2d offset = getOffset(index); + return RelativeLocation.getInstance() + .getRelativeLocation(location, + pivot.getX() - (offset.y * getComponentLength()), + pivot.getZ() + (offset.x * getComponentLength()), + pivot.getY()); + } + + public void setPivot(@NotNull Vector pivot) { + Objects.requireNonNull(pivot, "'pivot' cannot be null!"); + this.pivot = pivot; + } +} diff --git a/src/main/java/us/mytheria/bloblib/displayentity/SyncDisplayEntityAnimations.java b/src/main/java/us/mytheria/bloblib/displayentity/SyncDisplayEntityAnimations.java index ef01549..24b7623 100644 --- a/src/main/java/us/mytheria/bloblib/displayentity/SyncDisplayEntityAnimations.java +++ b/src/main/java/us/mytheria/bloblib/displayentity/SyncDisplayEntityAnimations.java @@ -6,10 +6,10 @@ public class SyncDisplayEntityAnimations { - private final double followSpeed, walkAwaySpeed, hoverSpeed, hoverHeightCeiling, + protected final double followSpeed, walkAwaySpeed, hoverSpeed, hoverHeightCeiling, hoverHeightFloor, yOffset; - private double hoverVelocity, hoverHeight; - private final DisplayEntity pet; + protected double hoverVelocity, hoverHeight; + protected final DisplayEntity pet; public SyncDisplayEntityAnimations(DisplayEntity pet, double followSpeed, @@ -37,11 +37,15 @@ public SyncDisplayEntityAnimations(DisplayEntity pet, } public Location move(Player player, Location loc) { - Vector goal = vectorFromLocation(player.getLocation()); + Location playerLocation = player.getLocation(); + double playerX = playerLocation.getX(); + double playerZ = playerLocation.getZ(); + Vector goal = vectorFromLocation(playerLocation); goal.setY(goal.getY() + yOffset); - double distance = Math.sqrt(Math.pow(loc.getX() - player.getLocation().getX(), 2) + Math.pow(loc.getZ() - player.getLocation().getZ(), 2)); + double distance = Math.sqrt(Math.pow(loc.getX() - playerX, 2) + + Math.pow(loc.getZ() - playerZ, 2)); if (distance < 2.5D) { - goal.setY(goal.getY() + player.getLocation().getY() - loc.getY()); + goal.setY(goal.getY() + playerLocation.getY() - loc.getY()); goal.setX(loc.getX()); goal.setZ(loc.getZ()); } @@ -49,11 +53,11 @@ public Location move(Player player, Location loc) { Vector direction = normalize(goal.subtract(start)); Location newLoc = loc.clone(); newLoc.add(direction.multiply(followSpeed)); - double a = player.getLocation().getX() - newLoc.getX(); - double b = player.getLocation().getZ() - newLoc.getZ(); + double a = playerX - newLoc.getX(); + double b = playerZ - newLoc.getZ(); double angle = Math.atan(b / a); angle = angle * (180 / Math.PI); - if (player.getLocation().getX() - newLoc.getX() >= 0) { + if (playerX - newLoc.getX() >= 0) { angle += 180; } angle += 270; diff --git a/src/main/java/us/mytheria/bloblib/displayentity/SyncDisplayPackEntityAnimations.java b/src/main/java/us/mytheria/bloblib/displayentity/SyncDisplayPackEntityAnimations.java new file mode 100644 index 0000000..0b77658 --- /dev/null +++ b/src/main/java/us/mytheria/bloblib/displayentity/SyncDisplayPackEntityAnimations.java @@ -0,0 +1,29 @@ +package us.mytheria.bloblib.displayentity; + +import org.bukkit.Location; +import org.bukkit.entity.Display; +import org.bukkit.entity.Player; + +public class SyncDisplayPackEntityAnimations extends SyncDisplayEntityAnimations { + private final PackMaster packMaster; + private final int index; + + public SyncDisplayPackEntityAnimations(DisplayEntity pet, + EntityAnimationsCarrier carrier, + PackMaster packMaster, + int index) { + super(pet, carrier); + this.packMaster = packMaster; + this.index = index; + } + + @Override + public Location move(Player player, Location loc) { + Location pivot = packMaster.pivot(player, index); + pivot.setPitch(Location.normalizePitch(0.0f)); + pivot.setY(player.getLocation().getY() + yOffset); + pet.teleport(pivot); + return pivot; + } + +} diff --git a/src/main/java/us/mytheria/bloblib/entities/BlobEditor.java b/src/main/java/us/mytheria/bloblib/entities/BlobEditor.java index 9fa545e..212412f 100644 --- a/src/main/java/us/mytheria/bloblib/entities/BlobEditor.java +++ b/src/main/java/us/mytheria/bloblib/entities/BlobEditor.java @@ -217,29 +217,6 @@ public void loadPage(int page, boolean whiteBackgroundRefill) { } } - /** - * loads the page with the given page number - * - * @param page the page number - * @param refill if the background should be refilled - * @param function the function to apply - */ - public void loadCustomPage(int page, boolean refill, Function function) { - if (page < 1) { - return; - } - if (getTotalPages() < page) { - return; - } - if (refill) - refillButton("White-Background"); - clearValues(); - List> values = this.customPage(page, getItemsPerPage(), function); - for (int i = 0; i < values.size(); i++) { - setValue(i, values.get(i)); - } - } - /** * returns the page with the given page number without loading * @@ -267,33 +244,6 @@ public List> page(int page, int itemsPerPage) { return values; } - /** - * returns specific page with provided function without loading - * - * @param page the page - * @param itemsPerPage the items per page - * @param function the function to apply - * @return the list of values - */ - public List> customPage(int page, int itemsPerPage, Function function) { - int start = (page - 1) * itemsPerPage; - int end = start + (itemsPerPage); - ArrayList> values = new ArrayList<>(); - for (int i = start; i < end; i++) { - T get; - try { - get = getList().get(i); - ItemStack itemStack = function.apply(get); - if (itemStack == null) - continue; - values.add(new VariableValue<>(itemStack, get)); - } catch (IndexOutOfBoundsException e) { - break; - } - } - return values; - } - /** * Selects an object from the editor. * @@ -389,6 +339,7 @@ public void add(T t) { /** * @return the list */ + @Override public List getList() { if (collection != null) { return new ArrayList<>(collection); diff --git a/src/main/java/us/mytheria/bloblib/entities/BlobSelector.java b/src/main/java/us/mytheria/bloblib/entities/BlobSelector.java index a035efb..885fc91 100644 --- a/src/main/java/us/mytheria/bloblib/entities/BlobSelector.java +++ b/src/main/java/us/mytheria/bloblib/entities/BlobSelector.java @@ -17,6 +17,7 @@ import java.util.*; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Supplier; /** * @author anjoismysign @@ -144,29 +145,6 @@ public void loadPage(int page, boolean whiteBackgroundRefill) { } } - /** - * loads the page with the given page number - * - * @param page the page number - * @param refill if the background should be refilled - * @param function the function to apply - */ - public void loadCustomPage(int page, boolean refill, Function function) { - if (page < 1) { - return; - } - if (getTotalPages() < page) { - return; - } - if (refill) - refillButton("White-Background"); - clearValues(); - List> values = this.customPage(page, getItemsPerPage(), function); - for (int i = 0; i < values.size(); i++) { - setValue(i, values.get(i)); - } - } - /** * returns the page with the given page number without loading * @@ -194,33 +172,6 @@ public List> page(int page, int itemsPerPage) { return values; } - /** - * returns specific page with provided function without loading - * - * @param page the page - * @param itemsPerPage the items per page - * @param function the function to apply - * @return the list of values - */ - public List> customPage(int page, int itemsPerPage, Function function) { - int start = (page - 1) * itemsPerPage; - int end = start + (itemsPerPage); - ArrayList> values = new ArrayList<>(); - for (int i = start; i < end; i++) { - T get; - try { - get = getList().get(i); - ItemStack itemStack = function.apply(get); - if (itemStack == null) - continue; - values.add(new VariableValue<>(itemStack, get)); - } catch (IndexOutOfBoundsException e) { - break; - } - } - return values; - } - public void selectElement(Player player, Consumer consumer, String timerMessageKey) { loadPage(getPage(), true); selectorManager.addSelectorListener(player, BlobSelectorListener.wise(player, @@ -228,8 +179,11 @@ public void selectElement(Player player, Consumer consumer, String timerMessa this)); } - public void selectElement(Player player, Consumer consumer, String timerMessageKey, Function function) { + public void selectElement(Player player, Consumer consumer, String timerMessageKey, Function function, + Supplier> selectorList) { loadCustomPage(getPage(), true, function); + setLoadFunction(function); + setCollectionSupplier(selectorList); selectorManager.addSelectorListener(player, BlobSelectorListener.wise(player, consumer, timerMessageKey, this)); @@ -257,6 +211,7 @@ public int getTotalPages() { /** * @return the list */ + @Override public List getList() { if (collection != null) { return new ArrayList<>(collection); diff --git a/src/main/java/us/mytheria/bloblib/entities/SquareMaster.java b/src/main/java/us/mytheria/bloblib/entities/SquareMaster.java new file mode 100644 index 0000000..198ec5f --- /dev/null +++ b/src/main/java/us/mytheria/bloblib/entities/SquareMaster.java @@ -0,0 +1,165 @@ +package us.mytheria.bloblib.entities; + +import me.anjoismysign.anjo.entities.Tuple2; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector2d; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class SquareMaster { + private double width, height; + private int rows, maxPerRow; + private final Map components; + private final Map indexes; + private double componentLength; + + public SquareMaster(int maxPerRow, + double componentLength, + @NotNull Map components) { + this.components = components; + this.indexes = new HashMap<>(); + this.maxPerRow = maxPerRow; + this.componentLength = componentLength; + } + + private void reload() { + int components = this.components.size(); + rows = (int) Math.ceil((double) components / maxPerRow); + width = Math.min(components, maxPerRow) * componentLength; + height = rows * componentLength; + } + + /** + * Merges the indexes with the master + * + * @param indexes - the indexes + */ + public void mergeIndexes(Map indexes) { + this.indexes.putAll(indexes); + } + + public void setComponentLength(double componentLength) { + this.componentLength = componentLength; + reload(); + } + + public void setMaxPerRow(int maxPerRow) { + this.maxPerRow = maxPerRow; + } + + public int getMaxPerRow() { + return maxPerRow; + } + + /** + * Adds a component to the master at the specified index (real index) + * + * @param component - the component + * @param realIndex - the index + * @return the index the component was added at + */ + public int addComponent(T component, int realIndex) { + components.put(realIndex, component); + int lowestMissing = getLowestMissing(indexes.keySet()); + indexes.put(lowestMissing, realIndex); + reload(); + return lowestMissing; + } + + private int getLowestMissing(Set indexes) { + int i = 0; + while (indexes.contains(i)) + i++; + return i; + } + + /** + * Removes a component from the master at the specified index (real index) + * + * @param index - the index + */ + public void removeComponent(int index) { + Integer real = indexes.get(index); + if (real == null) + return; + components.remove(real); + indexes.remove(index); + reload(); + } + + /** + * Gets the component at the specified index + * + * @param index - the index + * @return the component + */ + @Nullable + public Tuple2 getComponent(int index) { + Integer real = indexes.get(index); + if (real == null) + return null; + T component = components.get(real); + if (component == null) + return null; + return new Tuple2<>(real, component); + } + + /** + * Gets the index of the specified component + * + * @param realIndex - the real index + * @return the index + */ + public Integer getIndex(int realIndex) { + return indexes.entrySet().stream() + .filter(entry -> entry.getValue().equals(realIndex)) + .map(Map.Entry::getKey) + .findFirst() + .orElse(null); + } + + public Vector2d getOffset(int index) { + int row = getRow(index); + int column = getColumn(index); + double componentLength = getComponentLength(); + double x = column + (componentLength / 2); + double z = row + (componentLength / 2); + return new Vector2d(x, z); + } + + public double getComponentLength() { + return componentLength; + } + + public double getWidth() { + return width; + } + + public double getHeight() { + return height; + } + + public int getRows() { + return rows; + } + + public int getRow(int index) { + return index / maxPerRow; + } + + public int getColumn(int index) { + return index % maxPerRow; + } + + /** + * Gets an unmodifiable copy of the map of the indexes. + * + * @return the indexes + */ + public Map getIndexes() { + return Map.copyOf(indexes); + } +} diff --git a/src/main/java/us/mytheria/bloblib/entities/inventory/VariableSelector.java b/src/main/java/us/mytheria/bloblib/entities/inventory/VariableSelector.java index 888170d..5f6dcff 100644 --- a/src/main/java/us/mytheria/bloblib/entities/inventory/VariableSelector.java +++ b/src/main/java/us/mytheria/bloblib/entities/inventory/VariableSelector.java @@ -14,6 +14,7 @@ import java.util.*; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Supplier; /** * @param type of the variable @@ -30,6 +31,12 @@ public abstract class VariableSelector extends BlobInventory { private final UUID builderId; private final VariableFiller filler; private final Consumer returnAction; + @Nullable + private Function loadFunction; + @Nullable + private Supplier> collectionSupplier; + @Nullable + private String whiteBackgroundName; private int page; private int itemsPerPage; @@ -60,12 +67,35 @@ public static BlobInventory DEFAULT() { * @param builderId the id of the builder * @param dataType the data type * @param filler the filler to use + * @param returnAction the action to run when the return button is clicked */ public VariableSelector(BlobInventory blobInventory, UUID builderId, String dataType, VariableFiller filler, @Nullable Consumer returnAction) { + this(blobInventory, builderId, dataType, filler, returnAction, null, null, null); + } + + /** + * creates a new VariableSelector + * + * @param blobInventory the inventory to use + * @param builderId the id of the builder + * @param dataType the data type + * @param filler the filler to use + * @param returnAction the action to run when the return button is clicked + * @param loadFunction the function to use to convert the list to an itemstack + */ + public VariableSelector(BlobInventory blobInventory, UUID builderId, + String dataType, VariableFiller filler, + @Nullable Consumer returnAction, + @Nullable Function loadFunction, + @Nullable Supplier> collectionSupplier, + @Nullable String whiteBackgroundName) { super(blobInventory.getTitle(), blobInventory.getSize(), blobInventory.getButtonManager()); this.returnAction = returnAction == null ? HumanEntity::closeInventory : returnAction; + this.loadFunction = loadFunction; + this.collectionSupplier = collectionSupplier; + this.whiteBackgroundName = whiteBackgroundName; this.filler = filler; this.builderId = builderId; this.values = new HashMap<>(); @@ -75,7 +105,7 @@ public VariableSelector(BlobInventory blobInventory, UUID builderId, buildInventory(); this.page = 1; this.itemsPerPage = 1; - Set slots = getSlots("White-Background"); + Set slots = getSlots(getWhiteBackgroundName()); if (slots != null) setItemsPerPage(slots.size()); loadFirstPage(); @@ -101,7 +131,7 @@ public void loadPage(int page, boolean whiteBackgroundRefill) { return; } if (whiteBackgroundRefill) - refillButton("White-Background"); + refillButton(getWhiteBackgroundName()); values.clear(); List> values = filler.page(page, itemsPerPage); for (int i = 0; i < values.size(); i++) { @@ -124,7 +154,7 @@ public void loadCustomPage(int page, boolean refill, List list, Function> values = filler.customPage(page, itemsPerPage, list, function); for (int i = 0; i < values.size(); i++) { @@ -132,13 +162,39 @@ public void loadCustomPage(int page, boolean refill, List list, Function function) { + if (page < 1) { + return; + } + if (getTotalPages() < page) { + return; + } + if (refill) + refillButton(getWhiteBackgroundName()); + clearValues(); + List> values = this.customPage(page, getItemsPerPage(), function); + for (int i = 0; i < values.size(); i++) { + setValue(i, values.get(i)); + } + } + /** * loads the specified page * * @param page the page to load */ public void loadPage(int page) { - loadPage(page, true); + if (loadFunction != null && collectionSupplier != null) + loadCustomPage(page, true, loadFunction); + else + loadPage(page, true); } /** @@ -356,4 +412,60 @@ public void addValues(Collection collection) { public void setItemsPerPage(int itemsPerPage) { this.itemsPerPage = itemsPerPage; } + + /** + * @return the list + */ + public List getList() { + if (collectionSupplier == null) + return new ArrayList<>(); + return collectionSupplier.get().stream().toList(); + } + + /** + * returns specific page with provided function without loading + * + * @param page the page + * @param itemsPerPage the items per page + * @param function the function to apply + * @return the list of values + */ + public List> customPage(int page, int itemsPerPage, Function function) { + int start = (page - 1) * itemsPerPage; + int end = start + (itemsPerPage); + ArrayList> values = new ArrayList<>(); + for (int i = start; i < end; i++) { + T get; + try { + get = getList().get(i); + ItemStack itemStack = function.apply(get); + if (itemStack == null) + continue; + values.add(new VariableValue<>(itemStack, get)); + } catch (IndexOutOfBoundsException e) { + break; + } + } + return values; + } + + public void setLoadFunction(@NotNull Function loadFunction) { + Objects.requireNonNull(loadFunction, "loadFunction cannot be null"); + this.loadFunction = loadFunction; + } + + public void setCollectionSupplier(@NotNull Supplier> collectionSupplier) { + Objects.requireNonNull(collectionSupplier, "listSupplier cannot be null"); + this.collectionSupplier = collectionSupplier; + } + + public void setWhiteBackgroundName(@NotNull String whiteBackgroundName) { + Objects.requireNonNull(whiteBackgroundName, "whiteBackgroundName cannot be null"); + this.whiteBackgroundName = whiteBackgroundName; + } + + @NotNull + private String getWhiteBackgroundName() { + return whiteBackgroundName == null ? "White-Background" : whiteBackgroundName; + } } diff --git a/src/main/java/us/mytheria/bloblib/gists/RelativeLocation.java b/src/main/java/us/mytheria/bloblib/gists/RelativeLocation.java new file mode 100644 index 0000000..f76513a --- /dev/null +++ b/src/main/java/us/mytheria/bloblib/gists/RelativeLocation.java @@ -0,0 +1,71 @@ +package us.mytheria.bloblib.gists; + +import org.bukkit.Location; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * @author Wasabi_Thumbs (...) + */ +public class RelativeLocation { + private static RelativeLocation instance; + + public static RelativeLocation getInstance() { + if (instance == null) { + instance = new RelativeLocation(); + } + return instance; + } + + private final double epsilon = Math.ulp(1.0d) * 2d; + + private boolean isSignificant(double value) { + return Math.abs(value) >= epsilon; + } + + public Location getRelativeLocation(@NotNull Location location, + double forward, + double right) { + return getRelativeLocation(location, forward, right, 0d); + } + + public Location getRelativeLocation(@NotNull Location location, + double forward, + double right, + double up) { + Objects.requireNonNull(location, "'location' cannot be null!"); + Vector direction = null; + if (isSignificant(forward)) { + direction = location.getDirection(); + location.add(direction.clone().multiply(forward)); + } + boolean hasUp = isSignificant(up); + if (hasUp && direction == null) direction = location.getDirection(); + if (isSignificant(right) || hasUp) { + Vector rightDirection; + if (direction != null && isSignificant(Math.abs(direction.getY()) - 1)) { + rightDirection = direction.clone(); + double factor = Math.sqrt(1 - Math.pow(rightDirection.getY(), 2)); // a shortcut that lets us not normalize which is slow + double nx = -rightDirection.getZ() / factor; + double nz = rightDirection.getX() / factor; + rightDirection.setX(nx); + rightDirection.setY(0d); + rightDirection.setZ(nz); + } else { + float yaw = location.getYaw() + 90f; + double yawRad = yaw * (Math.PI / 180d); + double z = Math.cos(yawRad); + double x = -Math.sin(yawRad); + rightDirection = new Vector(x, 0d, z); + } + location.add(rightDirection.clone().multiply(right)); + if (hasUp) { + Vector upDirection = rightDirection.crossProduct(direction); + location.add(upDirection.clone().multiply(up)); + } + } + return location; + } +}