GUIParent & GUI-based Animations

This commit is contained in:
NichtStudioCode 2021-01-28 00:31:21 +01:00
parent 3eb57e9fae
commit e4980188e2
10 changed files with 272 additions and 135 deletions

@ -1,7 +1,7 @@
package de.studiocode.invgui.animation; package de.studiocode.invgui.animation;
import org.bukkit.entity.Player; import de.studiocode.invgui.gui.GUI;
import org.bukkit.inventory.Inventory; import de.studiocode.invgui.window.Window;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
@ -10,20 +10,19 @@ import java.util.function.BiConsumer;
public interface Animation { public interface Animation {
/** /**
* Sets the {@link Player} that will see this animation. * Sets the {@link GUI} this {@link Animation} will take place in.
* Useful for playing sounds in a showHandler. ({@link #addShowHandler(BiConsumer)})
* *
* @param player The {@link Player} that will se this {@link Animation}. * @param gui The {@link GUI} this {@link Animation} will take place in
*/ */
void setPlayer(@NotNull Player player); void setGUI(GUI gui);
/** /**
* Sets the bounds of the {@link Inventory} this {@link Animation} will take place in. * Sets the {@link Window}s that will see this animation.
* Useful for playing sounds in a showHandler. ({@link #addShowHandler(BiConsumer)})
* *
* @param width The width of the {@link Inventory} * @param windows The {@link Window}s that will see this animation
* @param height The height {@link Inventory}
*/ */
void setBounds(int width, int height); void setWindows(@NotNull List<Window> windows);
/** /**
* Sets the slots that should be shown. * Sets the slots that should be shown.

@ -2,41 +2,58 @@ package de.studiocode.invgui.animation.impl;
import de.studiocode.invgui.InvGui; import de.studiocode.invgui.InvGui;
import de.studiocode.invgui.animation.Animation; import de.studiocode.invgui.animation.Animation;
import de.studiocode.invgui.gui.GUI;
import de.studiocode.invgui.util.SlotUtils; import de.studiocode.invgui.util.SlotUtils;
import de.studiocode.invgui.window.Window;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.stream.Collectors;
public abstract class BaseAnimation implements Animation { public abstract class BaseAnimation implements Animation {
private final int tickDelay;
private final List<Runnable> finishHandlers = new ArrayList<>(); private final List<Runnable> finishHandlers = new ArrayList<>();
private final int tickDelay;
private GUI gui;
private int width; private int width;
private int height; private int height;
private int size;
private Player player; private List<Window> windows;
private CopyOnWriteArrayList<Integer> slots; private CopyOnWriteArrayList<Integer> slots;
private BiConsumer<Integer, Integer> show; private BiConsumer<Integer, Integer> show;
private BukkitTask task; private BukkitTask task;
private int frame; private int frame;
private int noViewerTicks;
public BaseAnimation(int tickDelay) { public BaseAnimation(int tickDelay) {
this.tickDelay = tickDelay; this.tickDelay = tickDelay;
} }
protected abstract void handleFrame(int frame); @Override
public void setGUI(GUI gui) {
this.gui = gui;
this.width = gui.getWidth();
this.height = gui.getHeight();
}
@Override @Override
public void setBounds(int width, int height) { public void setWindows(@NotNull List<Window> windows) {
this.width = width; this.windows = windows;
this.height = height; }
this.size = width * height;
@Override
public void setSlots(List<Integer> slots) {
this.slots = new CopyOnWriteArrayList<>(slots);
} }
@Override @Override
@ -45,22 +62,32 @@ public abstract class BaseAnimation implements Animation {
else this.show = show; else this.show = show;
} }
protected void show(int... slots) {
for (int i : slots) show.accept(frame, i);
}
@Override @Override
public void addFinishHandler(@NotNull Runnable finish) { public void addFinishHandler(@NotNull Runnable finish) {
finishHandlers.add(finish); finishHandlers.add(finish);
} }
public CopyOnWriteArrayList<Integer> getSlots() { @Override
return slots; public void start() {
task = Bukkit.getScheduler().runTaskTimer(InvGui.getInstance().getPlugin(), () -> {
// if there are now viewers for more than 3 ticks, the animation can be cancelled
if (getViewers().isEmpty()) {
noViewerTicks++;
if (noViewerTicks > 3) {
gui.cancelAnimation();
return;
}
} else noViewerTicks = 0;
// handle the next frame
handleFrame(frame);
frame++;
}, 0, tickDelay);
} }
@Override @Override
public void setSlots(List<Integer> slots) { public void cancel() {
this.slots = new CopyOnWriteArrayList<>(slots); task.cancel();
} }
protected void finish() { protected void finish() {
@ -68,11 +95,14 @@ public abstract class BaseAnimation implements Animation {
finishHandlers.forEach(Runnable::run); finishHandlers.forEach(Runnable::run);
} }
public void start() { protected abstract void handleFrame(int frame);
task = Bukkit.getScheduler().runTaskTimer(InvGui.getInstance().getPlugin(), () -> {
handleFrame(frame); public CopyOnWriteArrayList<Integer> getSlots() {
frame++; return slots;
}, 0, tickDelay); }
protected void show(int... slots) {
for (int i : slots) show.accept(frame, i);
} }
protected int convToIndex(int x, int y) { protected int convToIndex(int x, int y) {
@ -82,10 +112,6 @@ public abstract class BaseAnimation implements Animation {
return SlotUtils.convertToIndex(x, y, width); return SlotUtils.convertToIndex(x, y, width);
} }
public void cancel() {
task.cancel();
}
protected int getWidth() { protected int getWidth() {
return width; return width;
} }
@ -94,16 +120,14 @@ public abstract class BaseAnimation implements Animation {
return height; return height;
} }
protected int getSize() { public List<Player> getViewers() {
return size; return windows.stream()
} .map(window -> {
List<HumanEntity> viewers = window.getInventory().getViewers();
protected Player getPlayer() { return (Player) (viewers.isEmpty() ? null : viewers.get(0));
return player; })
} .filter(Objects::nonNull)
.collect(Collectors.toList());
public void setPlayer(@NotNull Player player) {
this.player = player;
} }
} }

@ -7,8 +7,8 @@ public abstract class SoundAnimation extends BaseAnimation {
public SoundAnimation(int tickDelay, boolean sound) { public SoundAnimation(int tickDelay, boolean sound) {
super(tickDelay); super(tickDelay);
if (sound) addShowHandler((frame, index) -> getPlayer().playSound(getPlayer().getLocation(), if (sound) addShowHandler((frame, index) -> getViewers().forEach(player ->
Sound.ENTITY_ITEM_PICKUP, 1, 1)); player.playSound(player.getLocation(), Sound.ENTITY_ITEM_PICKUP, 1, 1)));
} }
} }

@ -1,5 +1,6 @@
package de.studiocode.invgui.gui; package de.studiocode.invgui.gui;
import de.studiocode.invgui.animation.Animation;
import de.studiocode.invgui.gui.SlotElement.ItemStackHolder; import de.studiocode.invgui.gui.SlotElement.ItemStackHolder;
import de.studiocode.invgui.gui.impl.*; import de.studiocode.invgui.gui.impl.*;
import de.studiocode.invgui.item.Item; import de.studiocode.invgui.item.Item;
@ -11,6 +12,10 @@ import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
import java.util.function.Predicate;
/** /**
* A GUI is a container for width * height {@link SlotElement}s.<br> * A GUI is a container for width * height {@link SlotElement}s.<br>
@ -59,7 +64,7 @@ public interface GUI {
* @param y The y coordinate * @param y The y coordinate
* @param slotElement The {@link SlotElement} to be placed there. * @param slotElement The {@link SlotElement} to be placed there.
*/ */
void setSlotElement(int x, int y, @NotNull SlotElement slotElement); void setSlotElement(int x, int y, SlotElement slotElement);
/** /**
* Sets the {@link SlotElement} on these coordinates. * Sets the {@link SlotElement} on these coordinates.
@ -68,7 +73,7 @@ public interface GUI {
* @param index The slot index * @param index The slot index
* @param slotElement The {@link SlotElement} to be placed there. * @param slotElement The {@link SlotElement} to be placed there.
*/ */
void setSlotElement(int index, @NotNull SlotElement slotElement); void setSlotElement(int index, SlotElement slotElement);
/** /**
* Gets the {@link SlotElement} on these coordinates. * Gets the {@link SlotElement} on these coordinates.
@ -204,6 +209,40 @@ public interface GUI {
*/ */
void handleItemShift(InventoryClickEvent event); void handleItemShift(InventoryClickEvent event);
/**
* Adds a {@link GUIParent} to the set of {@link GUIParent}s.
*
* @param parent The {@link GUIParent} to add
*/
void addParent(@NotNull GUIParent parent);
/**
* Removes a {@link GUIParent} from the set of {@link GUIParent}s
*
* @param parent The {@link GUIParent} to remove
*/
void removeParent(@NotNull GUIParent parent);
/**
* Gets all {@link GUIParent}s.
*
* @return The {@link GUIParent}s of this {@link GUI}
*/
Set<GUIParent> getParents();
/**
* Plays an {@link Animation}.
*
* @param animation The {@link Animation} to play.
* @param filter The filter that selects which {@link ItemStackHolder}s should be animated.
*/
void playAnimation(@NotNull Animation animation, @Nullable Predicate<ItemStackHolder> filter);
/**
* Cancels the running {@link Animation} if there is one.
*/
void cancelAnimation();
// ---- fill methods ---- // ---- fill methods ----
/** /**

@ -0,0 +1,13 @@
package de.studiocode.invgui.gui;
public interface GUIParent {
/**
* Called by the child {@link GUI} to report an update of a {@link SlotElement}.
*
* @param child The child {@link GUI} whose {@link SlotElement} has changed
* @param slotIndex The slot index of the changed {@link SlotElement} in the child {@link GUI}
*/
void handleSlotElementUpdate(GUI child, int slotIndex);
}

@ -28,7 +28,7 @@ public abstract class BaseGUI extends IndexedGUI {
} }
@Override @Override
public void setSlotElement(int x, int y, @NotNull SlotElement slotElement) { public void setSlotElement(int x, int y, SlotElement slotElement) {
setSlotElement(convToIndex(x, y), slotElement); setSlotElement(convToIndex(x, y), slotElement);
} }
@ -81,7 +81,7 @@ public abstract class BaseGUI extends IndexedGUI {
public void fill(@NotNull SortedSet<Integer> slots, Item item, boolean replaceExisting) { public void fill(@NotNull SortedSet<Integer> slots, Item item, boolean replaceExisting) {
for (int slot : slots) { for (int slot : slots) {
if (!replaceExisting && slotElements[slot] != null) continue; if (!replaceExisting && hasSlotElement(slot)) continue;
setItem(slot, item); setItem(slot, item);
} }
} }
@ -89,14 +89,14 @@ public abstract class BaseGUI extends IndexedGUI {
@Override @Override
public void fill(int start, int end, Item item, boolean replaceExisting) { public void fill(int start, int end, Item item, boolean replaceExisting) {
for (int i = start; i < end; i++) { for (int i = start; i < end; i++) {
if (!replaceExisting && slotElements[i] != null) continue; if (!replaceExisting && hasSlotElement(i)) continue;
setItem(i, item); setItem(i, item);
} }
} }
@Override @Override
public void fill(Item item, boolean replaceExisting) { public void fill(Item item, boolean replaceExisting) {
fill(0, size, item, replaceExisting); fill(0, getSize(), item, replaceExisting);
} }
@Override @Override

@ -1,6 +1,8 @@
package de.studiocode.invgui.gui.impl; package de.studiocode.invgui.gui.impl;
import de.studiocode.invgui.animation.Animation;
import de.studiocode.invgui.gui.GUI; import de.studiocode.invgui.gui.GUI;
import de.studiocode.invgui.gui.GUIParent;
import de.studiocode.invgui.gui.SlotElement; import de.studiocode.invgui.gui.SlotElement;
import de.studiocode.invgui.gui.SlotElement.ItemSlotElement; import de.studiocode.invgui.gui.SlotElement.ItemSlotElement;
import de.studiocode.invgui.gui.SlotElement.ItemStackHolder; import de.studiocode.invgui.gui.SlotElement.ItemStackHolder;
@ -10,19 +12,26 @@ import de.studiocode.invgui.item.Item;
import de.studiocode.invgui.util.ArrayUtils; import de.studiocode.invgui.util.ArrayUtils;
import de.studiocode.invgui.virtualinventory.VirtualInventory; import de.studiocode.invgui.virtualinventory.VirtualInventory;
import de.studiocode.invgui.virtualinventory.event.ItemUpdateEvent; import de.studiocode.invgui.virtualinventory.event.ItemUpdateEvent;
import de.studiocode.invgui.window.Window;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType; import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*; import java.util.*;
import java.util.function.Predicate;
abstract class IndexedGUI implements GUI { abstract class IndexedGUI implements GUI, GUIParent {
protected final int size; private final int size;
protected final SlotElement[] slotElements; private final SlotElement[] slotElements;
private final Set<GUIParent> parents = new HashSet<>();
private SlotElement[] animationElements;
private Animation animation;
public IndexedGUI(int size) { public IndexedGUI(int size) {
this.size = size; this.size = size;
@ -31,6 +40,12 @@ abstract class IndexedGUI implements GUI {
@Override @Override
public void handleClick(int slotNumber, Player player, ClickType clickType, InventoryClickEvent event) { public void handleClick(int slotNumber, Player player, ClickType clickType, InventoryClickEvent event) {
if (animation != null) {
// cancel all clicks if an animation is running
event.setCancelled(true);
return;
}
SlotElement slotElement = slotElements[slotNumber]; SlotElement slotElement = slotElements[slotNumber];
if (slotElement instanceof LinkedSlotElement) { if (slotElement instanceof LinkedSlotElement) {
LinkedSlotElement linkedElement = (LinkedSlotElement) slotElement; LinkedSlotElement linkedElement = (LinkedSlotElement) slotElement;
@ -123,6 +138,8 @@ abstract class IndexedGUI implements GUI {
public void handleItemShift(InventoryClickEvent event) { public void handleItemShift(InventoryClickEvent event) {
event.setCancelled(true); event.setCancelled(true);
if (animation != null) return; // cancel all clicks if an animation is running
Player player = (Player) event.getWhoClicked(); Player player = (Player) event.getWhoClicked();
ItemStack clicked = event.getCurrentItem(); ItemStack clicked = event.getCurrentItem();
@ -157,8 +174,119 @@ abstract class IndexedGUI implements GUI {
} }
@Override @Override
public void setSlotElement(int index, @NotNull SlotElement slotElement) { public void handleSlotElementUpdate(GUI child, int slotIndex) {
// find all SlotElements that link to this slotIndex in this child GUI and notify all parents
for (int index = 0; index < size; index++) {
SlotElement element = slotElements[index];
if (element instanceof LinkedSlotElement) {
LinkedSlotElement linkedSlotElement = (LinkedSlotElement) element;
if (linkedSlotElement.getGui() == child && linkedSlotElement.getSlotIndex() == slotIndex)
for (GUIParent parent : parents) parent.handleSlotElementUpdate(this, index);
}
}
}
@Override
public void addParent(@NotNull GUIParent parent) {
parents.add(parent);
}
@Override
public void removeParent(@NotNull GUIParent parent) {
parents.remove(parent);
}
@Override
public Set<GUIParent> getParents() {
return parents;
}
private List<Window> findAllWindows() {
List<Window> windows = new ArrayList<>();
List<GUIParent> parents = new ArrayList<>(this.parents);
while (!parents.isEmpty()) {
List<GUIParent> parents1 = new ArrayList<>(parents);
parents.clear();
for (GUIParent parent : parents1) {
if (parent instanceof GUI) parents.addAll(((GUI) parent).getParents());
else if (parent instanceof Window) windows.add((Window) parent);
}
}
return windows;
}
@Override
public void playAnimation(@NotNull Animation animation, @Nullable Predicate<ItemStackHolder> filter) {
if (animation != null) cancelAnimation();
this.animation = animation;
this.animationElements = slotElements.clone();
List<Integer> slots = new ArrayList<>();
for (int i = 0; i < size; i++) {
ItemStackHolder holder = getItemStackHolder(i);
if (holder != null && (filter == null || filter.test(holder))) {
slots.add(i);
setSlotElement(i, null);
}
}
animation.setSlots(slots);
animation.setGUI(this);
animation.setWindows(findAllWindows());
animation.addShowHandler((frame, index) -> setSlotElement(index, animationElements[index]));
animation.addFinishHandler(() -> {
this.animation = null;
this.animationElements = null;
});
animation.start();
}
@Override
public void cancelAnimation() {
if (this.animation != null) {
// cancel the scheduler task and set animation to null
animation.cancel();
animation = null;
// show all SlotElements again
for (int i = 0; i < size; i++) setSlotElement(i, animationElements[i]);
animationElements = null;
}
}
@Override
public void setSlotElement(int index, SlotElement slotElement) {
SlotElement oldElement = slotElements[index];
GUI oldLink = oldElement instanceof LinkedSlotElement ? ((LinkedSlotElement) oldElement).getGui() : null;
// set new SlotElement on index
slotElements[index] = slotElement; slotElements[index] = slotElement;
GUI newLink = slotElement instanceof LinkedSlotElement ? ((LinkedSlotElement) slotElement).getGui() : null;
// notify parents that a SlotElement has been changed
parents.forEach(parent -> parent.handleSlotElementUpdate(this, index));
// if newLink is the same as oldLink, there isn't anything to be done
if (newLink == oldLink) return;
// if the slot previously linked to GUI
if (oldLink != null) {
// If no other slot still links to that GUI, remove this GUI from parents
if (Arrays.stream(slotElements)
.filter(element -> element instanceof LinkedSlotElement)
.map(element -> ((LinkedSlotElement) element).getGui())
.noneMatch(gui -> gui == oldLink)) oldLink.removeParent(this);
}
// if the slot now links to a GUI add this as parent
if (newLink != null) {
newLink.addParent(this);
}
} }
@Override @Override
@ -179,7 +307,7 @@ abstract class IndexedGUI implements GUI {
@Override @Override
public void setItem(int index, Item item) { public void setItem(int index, Item item) {
remove(index); remove(index);
if (item != null) slotElements[index] = new ItemSlotElement(item); if (item != null) setSlotElement(index, new ItemSlotElement(item));
} }
@Override @Override
@ -211,7 +339,7 @@ abstract class IndexedGUI implements GUI {
@Override @Override
public void remove(int index) { public void remove(int index) {
slotElements[index] = null; setSlotElement(index, null);
} }
@Override @Override

@ -1,8 +1,6 @@
package de.studiocode.invgui.window; package de.studiocode.invgui.window;
import de.studiocode.invgui.animation.Animation;
import de.studiocode.invgui.gui.GUI; import de.studiocode.invgui.gui.GUI;
import de.studiocode.invgui.gui.SlotElement.ItemStackHolder;
import de.studiocode.invgui.item.Item; import de.studiocode.invgui.item.Item;
import de.studiocode.invgui.item.itembuilder.ItemBuilder; import de.studiocode.invgui.item.itembuilder.ItemBuilder;
import de.studiocode.invgui.virtualinventory.VirtualInventory; import de.studiocode.invgui.virtualinventory.VirtualInventory;
@ -15,15 +13,12 @@ import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID; import java.util.UUID;
import java.util.function.Predicate;
/** /**
* A window is the way to show a player a GUI. * A window is the way to show a player a GUI.
* Windows can only have one viewer at a time. * Windows can only have one viewer.
* *
* @see BaseWindow * @see BaseWindow
* @see NormalInventoryWindow * @see NormalInventoryWindow
@ -46,12 +41,6 @@ public interface Window {
*/ */
GUI getGui(); GUI getGui();
/**
* A method called every tick by the {@link WindowManager}
* to re-draw the {@link Item}s.
*/
void handleTick();
/** /**
* A method called by the {@link WindowManager} to notify the Window * A method called by the {@link WindowManager} to notify the Window
* that one of its {@link Item}s has been clicked. * that one of its {@link Item}s has been clicked.
@ -126,14 +115,6 @@ public interface Window {
*/ */
void show(); void show();
/**
* Plays an animation.
*
* @param animation The animation to play.
* @param filter The filter that selects which Items should be animated.
*/
void playAnimation(@NotNull Animation animation, @Nullable Predicate<ItemStackHolder> filter);
/** /**
* Gets if the player is able to close the {@link Inventory}. * Gets if the player is able to close the {@link Inventory}.
* *

@ -27,8 +27,6 @@ public class WindowManager implements Listener {
private WindowManager() { private WindowManager() {
Bukkit.getPluginManager().registerEvents(this, InvGui.getInstance().getPlugin()); Bukkit.getPluginManager().registerEvents(this, InvGui.getInstance().getPlugin());
Bukkit.getScheduler().scheduleSyncRepeatingTask(InvGui.getInstance().getPlugin(),
() -> windows.forEach(Window::handleTick), 0, 1);
InvGui.getInstance().addDisableHandler(() -> windows.forEach(w -> w.close(true))); InvGui.getInstance().addDisableHandler(() -> windows.forEach(w -> w.close(true)));
} }

@ -1,8 +1,8 @@
package de.studiocode.invgui.window.impl; package de.studiocode.invgui.window.impl;
import de.studiocode.invgui.InvGui; import de.studiocode.invgui.InvGui;
import de.studiocode.invgui.animation.Animation;
import de.studiocode.invgui.gui.GUI; import de.studiocode.invgui.gui.GUI;
import de.studiocode.invgui.gui.GUIParent;
import de.studiocode.invgui.gui.SlotElement.ItemSlotElement; import de.studiocode.invgui.gui.SlotElement.ItemSlotElement;
import de.studiocode.invgui.gui.SlotElement.ItemStackHolder; import de.studiocode.invgui.gui.SlotElement.ItemStackHolder;
import de.studiocode.invgui.gui.SlotElement.VISlotElement; import de.studiocode.invgui.gui.SlotElement.VISlotElement;
@ -18,13 +18,10 @@ import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*; import java.util.*;
import java.util.function.Predicate;
public abstract class BaseWindow implements Window { public abstract class BaseWindow implements Window, GUIParent {
private final GUI gui; private final GUI gui;
private final int size; private final int size;
@ -33,7 +30,6 @@ public abstract class BaseWindow implements Window {
private final boolean closeOnEvent; private final boolean closeOnEvent;
private final ItemStackHolder[] itemsDisplayed; private final ItemStackHolder[] itemsDisplayed;
private Animation animation;
private boolean closeable; private boolean closeable;
private boolean closed; private boolean closed;
@ -46,6 +42,7 @@ public abstract class BaseWindow implements Window {
this.closeOnEvent = closeOnEvent; this.closeOnEvent = closeOnEvent;
this.itemsDisplayed = new ItemStackHolder[size]; this.itemsDisplayed = new ItemStackHolder[size];
gui.addParent(this);
initItems(); initItems();
WindowManager.getInstance().addWindow(this); WindowManager.getInstance().addWindow(this);
} }
@ -95,25 +92,18 @@ public abstract class BaseWindow implements Window {
} }
@Override @Override
public void handleTick() { public void handleSlotElementUpdate(GUI child, int slotIndex) {
for (int i = 0; i < size; i++) { redrawItem(slotIndex, gui.getItemStackHolder(slotIndex), true);
ItemStackHolder holder = gui.getItemStackHolder(i);
if (itemsDisplayed[i] != holder) redrawItem(i, holder, true);
}
} }
@Override @Override
public void handleClick(InventoryClickEvent event) { public void handleClick(InventoryClickEvent event) {
if (animation == null) { // if not in animation, let the gui handle the click
gui.handleClick(event.getSlot(), (Player) event.getWhoClicked(), event.getClick(), event); gui.handleClick(event.getSlot(), (Player) event.getWhoClicked(), event.getClick(), event);
} else event.setCancelled(true);
} }
@Override @Override
public void handleItemShift(InventoryClickEvent event) { public void handleItemShift(InventoryClickEvent event) {
if (animation == null) { // if not in animation, let the gui handle the item shift
gui.handleItemShift(event); gui.handleItemShift(event);
} else event.setCancelled(true);
} }
@Override @Override
@ -125,7 +115,6 @@ public abstract class BaseWindow implements Window {
@Override @Override
public void handleClose(Player player) { public void handleClose(Player player) {
if (closeable) { if (closeable) {
stopAnimation();
if (closeOnEvent) close(false); if (closeOnEvent) close(false);
} else { } else {
if (player.equals(getViewer())) if (player.equals(getViewer()))
@ -168,6 +157,8 @@ public abstract class BaseWindow implements Window {
.map(holder -> ((ItemSlotElement) holder).getItem()) .map(holder -> ((ItemSlotElement) holder).getItem())
.forEach(item -> item.removeWindow(this)); .forEach(item -> item.removeWindow(this));
gui.removeParent(this);
if (closeForViewer) closeForViewer(); if (closeForViewer) closeForViewer();
} }
@ -187,42 +178,6 @@ public abstract class BaseWindow implements Window {
viewer.openInventory(inventory); viewer.openInventory(inventory);
} }
@Override
public void playAnimation(@NotNull Animation animation, @Nullable Predicate<ItemStackHolder> filter) {
if (getViewer() != null) {
this.animation = animation;
List<Integer> slots = new ArrayList<>();
for (int i = 0; i < size; i++) {
ItemStackHolder holder = itemsDisplayed[i];
if (holder != null && (filter == null || filter.test(holder))) {
slots.add(i);
inventory.setItem(i, null);
}
}
animation.setBounds(getGui().getWidth(), getGui().getHeight());
animation.setPlayer(getViewer());
animation.addShowHandler((frame, index) -> redrawItem(index, itemsDisplayed[index], false));
animation.addFinishHandler(() -> this.animation = null);
animation.setSlots(slots);
animation.start();
}
}
private void stopAnimation() {
if (this.animation != null) {
// cancel the scheduler task and set animation to null
animation.cancel();
animation = null;
// show all items again
for (int i = 0; i < gui.getSize(); i++)
redrawItem(i, itemsDisplayed[i], false);
}
}
@Override @Override
public Player getViewer() { public Player getViewer() {
return Bukkit.getPlayer(viewerUUID); return Bukkit.getPlayer(viewerUUID);