diff --git a/core/src/main/java/dev/triumphteam/gui/animations/Animation.java b/core/src/main/java/dev/triumphteam/gui/animations/Animation.java new file mode 100644 index 00000000..30841eb6 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/animations/Animation.java @@ -0,0 +1,93 @@ +/* + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.triumphteam.gui.animations; + +import dev.triumphteam.gui.animations.impl.SimpleAnimation; +import dev.triumphteam.gui.animations.impl.InfiniteAnimation; +import dev.triumphteam.gui.guis.BaseGui; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; + +public interface Animation { + + /** + * Reset to the initial frame + */ + void reset(); + + /** + * Go to the next frame + * @return the next frame or null if it is the last frame + */ + @Nullable Frame nextFrame(); + + /** + * Start the animation + * @param gui The gui to apply the animation to + */ + void start(BaseGui gui); + + /** + * Stops the animation + */ + void stop(); + + /** + * @return the delay between frames + */ + int getDelay(); + + /** + * @return the bukkit runnable that handles frame updates + */ + AnimationRunner getRunnable(); + + /** + * @return the gui that this animation is applyed to + */ + BaseGui getGui(); + + /** + * Create a simple animation that stop at the end of the frames + * @param delay the delay between frames + * @param frames the frames + * @return the animation + */ + static @NotNull Animation of(int delay, Frame... frames) { + return new SimpleAnimation(delay, Arrays.asList(frames)); + } + + /** + * Create a loop animation + * @param delay the delay between frames + * @param frames the frames + * @return the animation + */ + static @NotNull Animation infinite(int delay, Frame... frames) { + return new InfiniteAnimation(delay, Arrays.asList(frames)); + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/animations/AnimationRunner.java b/core/src/main/java/dev/triumphteam/gui/animations/AnimationRunner.java new file mode 100644 index 00000000..760fc945 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/animations/AnimationRunner.java @@ -0,0 +1,50 @@ +/* + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.triumphteam.gui.animations; + +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitRunnable; +import org.jetbrains.annotations.NotNull; + +public class AnimationRunner extends BukkitRunnable { + private static final Plugin plugin = JavaPlugin.getProvidingPlugin(AnimationRunner.class); + private final Animation animation; + + public AnimationRunner(@NotNull Animation animation) { + this.animation = animation; + + this.runTaskTimerAsynchronously(plugin,animation.getDelay(),animation.getDelay()); + } + + @Override + public void run() { + Frame frame = animation.nextFrame(); + if (frame == null) { + this.cancel(); + } + } + +} diff --git a/core/src/main/java/dev/triumphteam/gui/animations/Frame.java b/core/src/main/java/dev/triumphteam/gui/animations/Frame.java new file mode 100644 index 00000000..fb68f6ff --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/animations/Frame.java @@ -0,0 +1,98 @@ +/* + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.triumphteam.gui.animations; + +import dev.triumphteam.gui.guis.BaseGui; +import dev.triumphteam.gui.guis.GuiItem; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +public class Frame { + private boolean saved = false; + private final Map defaultItems; + private final Map items; + + /** + * @param items A map with the slot and the item to display in this frame + */ + public Frame(Map items) { + this.items = items; + this.defaultItems = new HashMap<>(); + } + + /** + * @param gui The gui to display this frame + * @return The same frame instance + */ + public Frame apply(@NotNull BaseGui gui) { + if (!saved) { + for (Map.Entry entry : items.entrySet()) { + defaultItems.put(entry.getKey(), gui.getGuiItem(entry.getKey())); + } + + saved = true; + } + + items.forEach(gui::updateItem); + return this; + } + + public void fallback(@NotNull BaseGui gui) { + defaultItems.forEach(gui::updateItem); + } + + public Map getItems() { + return items; + } + + @Override + public String toString() { + return "Frame{" + + "items=" + items + + '}'; + } + + @Contract(value = " -> new", pure = true) + public static @NotNull Builder builder() { + return new Builder(); + } + + public static class Builder { + private final Map items = new HashMap<>(); + + public Builder addItem(int slot, GuiItem item) { + items.put(slot, item); + return this; + } + + @Contract(value = " -> new", pure = true) + public @NotNull Frame build() { + return new Frame(items); + } + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/animations/impl/BaseAnimation.java b/core/src/main/java/dev/triumphteam/gui/animations/impl/BaseAnimation.java new file mode 100644 index 00000000..13b3604d --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/animations/impl/BaseAnimation.java @@ -0,0 +1,112 @@ +/* + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.triumphteam.gui.animations.impl; + +import dev.triumphteam.gui.animations.Animation; +import dev.triumphteam.gui.animations.AnimationRunner; +import dev.triumphteam.gui.animations.Frame; +import dev.triumphteam.gui.guis.BaseGui; +import org.apache.commons.lang3.Validate; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public abstract class BaseAnimation implements Animation { + protected int current; + protected BaseGui gui; + protected AnimationRunner runner; + protected final int delay; + protected List frames; + + public BaseAnimation(int delay, List frames) { + this.delay = delay; + this.frames = frames; + + this.reset(); + } + + @Override + public void reset() { + current = 0; + } + + @Override + public @Nullable Frame nextFrame() { + Validate.notNull(gui, "Gui has not been linked to this animation."); + current++; + + if (frames.size() == current) { + return this.onFinish(); + } + + return frames.get(current).apply(gui); + } + + @Override + public void start(BaseGui gui) { + Validate.notNull(gui, "Gui cannot be null."); + this.gui = gui; + + this.reset(); + frames.get(current).apply(gui); + + if (runner != null) { + runner.cancel(); + runner = null; + } + + this.runner = new AnimationRunner(this); + } + + @Override + public int getDelay() { + return delay; + } + + @Override + public BaseGui getGui() { + return gui; + } + + @Override + public AnimationRunner getRunnable() { + return runner; + } + + @Override + public void stop() { + Validate.notNull(gui, "Gui has not been linked to this animation. Please link it by calling the to(BaseGui) method"); + + if (this.runner != null && !this.runner.isCancelled()) { + this.runner.cancel(); + } + frames.get(0).fallback(gui); + } + + /** + * @return The frame to return when the animation is finished + */ + public abstract Frame onFinish(); +} diff --git a/core/src/main/java/dev/triumphteam/gui/animations/impl/InfiniteAnimation.java b/core/src/main/java/dev/triumphteam/gui/animations/impl/InfiniteAnimation.java new file mode 100644 index 00000000..4cb3ee21 --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/animations/impl/InfiniteAnimation.java @@ -0,0 +1,43 @@ +/* + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.triumphteam.gui.animations.impl; + +import dev.triumphteam.gui.animations.Frame; + +import java.util.List; + +public class InfiniteAnimation extends BaseAnimation { + + public InfiniteAnimation(int delay, List frames) { + super(delay, frames); + } + + @Override + public Frame onFinish() { + this.reset(); + + return this.frames.get(current).apply(gui); + } +} diff --git a/core/src/main/java/dev/triumphteam/gui/animations/impl/SimpleAnimation.java b/core/src/main/java/dev/triumphteam/gui/animations/impl/SimpleAnimation.java new file mode 100644 index 00000000..166e587c --- /dev/null +++ b/core/src/main/java/dev/triumphteam/gui/animations/impl/SimpleAnimation.java @@ -0,0 +1,44 @@ +/* + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.triumphteam.gui.animations.impl; + +import dev.triumphteam.gui.animations.Frame; + +import java.util.List; + +public class SimpleAnimation extends BaseAnimation { + + public SimpleAnimation(int delay, List frames) { + super(delay, frames); + } + + @Override + public Frame onFinish() { + this.stop(); + + return null; + } + +} diff --git a/core/src/main/java/dev/triumphteam/gui/guis/BaseGui.java b/core/src/main/java/dev/triumphteam/gui/guis/BaseGui.java index 4eaa49e3..1bb8fdeb 100644 --- a/core/src/main/java/dev/triumphteam/gui/guis/BaseGui.java +++ b/core/src/main/java/dev/triumphteam/gui/guis/BaseGui.java @@ -24,6 +24,7 @@ package dev.triumphteam.gui.guis; import dev.triumphteam.gui.TriumphGui; +import dev.triumphteam.gui.animations.Animation; import dev.triumphteam.gui.components.GuiContainer; import dev.triumphteam.gui.components.GuiAction; import dev.triumphteam.gui.components.GuiType; @@ -50,13 +51,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** @@ -90,6 +85,10 @@ public abstract class BaseGui implements InventoryHolder { private final GuiFiller filler = new GuiFiller(this); // Contains all items the GUI will have. private final Map guiItems; + + private final List animations = new ArrayList<>(); + private boolean animationsStarted = false; + // Actions for specific slots. private final Map> slotActions; // Interaction modifiers. @@ -295,6 +294,10 @@ public void addItem(final boolean expandIfFull, @NotNull final GuiItem... items) this.addItem(true, notAddedItems.toArray(new GuiItem[0])); } + public void addAnimation(Animation animation) { + this.animations.add(animation); + } + /** * Adds a {@link GuiAction} for when clicking on a specific slot. * See {@link InventoryClickEvent}. @@ -360,6 +363,11 @@ public void open(@NotNull final HumanEntity player) { inventory.clear(); populateGui(); player.openInventory(inventory); + + if (!animationsStarted) { + animations.forEach((animation) -> animation.start(this)); + animationsStarted = true; + } } /** @@ -380,6 +388,8 @@ public void close(@NotNull final HumanEntity player) { public void close(@NotNull final HumanEntity player, final boolean runCloseAction) { final Runnable task = () -> { this.runCloseAction = runCloseAction; + this.animations.forEach(Animation::stop); + this.animationsStarted = false; player.closeInventory(); this.runCloseAction = true; }; @@ -898,6 +908,11 @@ GuiAction getSlotAction(final int slot) { return slotActions.get(slot); } + @NotNull + List getAnimations() { + return animations; + } + /** * Populates the GUI with it's items. */ diff --git a/core/src/main/java/dev/triumphteam/gui/guis/GuiListener.java b/core/src/main/java/dev/triumphteam/gui/guis/GuiListener.java index 558dc9df..5a5ace8a 100644 --- a/core/src/main/java/dev/triumphteam/gui/guis/GuiListener.java +++ b/core/src/main/java/dev/triumphteam/gui/guis/GuiListener.java @@ -23,6 +23,7 @@ */ package dev.triumphteam.gui.guis; +import dev.triumphteam.gui.animations.Animation; import dev.triumphteam.gui.components.GuiAction; import dev.triumphteam.gui.components.util.ItemNbt; import org.bukkit.event.EventHandler; @@ -130,6 +131,7 @@ public void onGuiClose(final InventoryCloseEvent event) { // GUI final BaseGui gui = (BaseGui) event.getInventory().getHolder(); + gui.getAnimations().forEach(Animation::stop); // The GUI action for closing final GuiAction closeAction = gui.getCloseGuiAction();