Skip to content

Commit a08074a

Browse files
Allow defining intermediary elements in animations
1 parent 9672945 commit a08074a

4 files changed

Lines changed: 133 additions & 21 deletions

File tree

invui/src/main/java/xyz/xenondevs/invui/gui/AbstractGui.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ public void playAnimation(Animation animation) {
506506
this.animationElements = slotElements.clone();
507507
animationImpl.bind(this);
508508
for (Slot slot : animationImpl.getRemainingSlots()) {
509-
setSlotElement(slot.x(), slot.y(), null);
509+
setSlotElement(slot.x(), slot.y(), animationImpl.getIntermediarySlotElement(slot));
510510
}
511511
animationImpl.addShowHandler(slots -> {
512512
for (Slot slot : slots) {

invui/src/main/java/xyz/xenondevs/invui/gui/Animation.java

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package xyz.xenondevs.invui.gui;
22

33
import org.bukkit.Sound;
4+
import org.jspecify.annotations.Nullable;
45
import xyz.xenondevs.invui.internal.util.ArrayUtils;
6+
import xyz.xenondevs.invui.item.Item;
7+
import xyz.xenondevs.invui.item.ItemProvider;
58

69
import java.util.Set;
710
import java.util.function.BiConsumer;
@@ -16,6 +19,8 @@
1619
* <ul>
1720
* <li>Slot filter: Defines which slots are part of the animation.</li>
1821
* <li>Slot selector: Selects the slots that are shown in each frame.</li>
22+
* <li>Intermediary generator: Creates intermediary {@link SlotElement SlotElements} that are displayed before
23+
* the slots pop in.</li>
1924
* <li>Show handler: Called when slot(s) are shown, for example to play a sound effect.</li>
2025
* <li>Finish handler: Called when the animation is finished.</li>
2126
* </ul>
@@ -168,7 +173,7 @@ sealed interface Builder permits AnimationImpl.BuilderImpl {
168173
* @param filter The filter defining which slots are part of the animation.
169174
* @return This {@link Builder Animation builder}
170175
*/
171-
Builder addSlotFilter(BiPredicate<Gui, Slot> filter);
176+
Builder addSlotFilter(BiPredicate<? super Gui, ? super Slot> filter);
172177

173178
/**
174179
* Adds a {@link #addSlotFilter(BiPredicate) filter} that ignores all empty slots.
@@ -217,23 +222,67 @@ default Builder addSoundEffect(Sound sound, float volume, float pitch) {
217222
* @param selector The slot selector for the animation.
218223
* @return This {@link Builder Animation builder}
219224
*/
220-
Builder setSlotSelector(Function<Animation, Set<Slot>> selector);
225+
Builder setSlotSelector(Function<? super Animation, ? extends Set<? extends Slot>> selector);
226+
227+
/**
228+
* Sets the intermediary {@link ItemProvider} that is displayed before the slots pop in.
229+
*
230+
* @param provider The intermediary {@link ItemProvider}.
231+
* @return This {@link Builder Animation builder}
232+
*/
233+
default Builder setIntermediary(ItemProvider provider) {
234+
return setIntermediary(Item.simple(provider));
235+
}
236+
237+
/**
238+
* Sets the intermediary {@link Item} that is displayed before the slots pop in.
239+
*
240+
* @param item The intermediary {@link Item}.
241+
* @return This {@link Builder Animation builder}
242+
*/
243+
default Builder setIntermediary(Item item) {
244+
var element = new SlotElement.Item(item);
245+
return setIntermediaryElementGenerator(slot -> element);
246+
}
247+
248+
/**
249+
* Sets the generator function that creates intermediary {@link ItemProvider ItemProviders} that are displayed
250+
* before the {@link Gui Gui's} slots pop in.
251+
*
252+
* @param intermediaryGenerator The generator function that creates intermediary {@link ItemProvider ItemProviders}.
253+
* @return This {@link Builder Animation builder}
254+
*/
255+
default Builder setIntermediaryGenerator(Function<? super Slot, ? extends @Nullable ItemProvider> intermediaryGenerator) {
256+
return setIntermediaryElementGenerator(slot -> {
257+
ItemProvider provider = intermediaryGenerator.apply(slot);
258+
return provider != null ? new SlotElement.Item(Item.simple(provider)) : null;
259+
});
260+
}
261+
262+
/**
263+
* Sets the generator function that creates intermediary {@link SlotElement SlotElements} that are displayed
264+
* before the {@link Gui Gui's} slots pop in.
265+
*
266+
* @param intermediaryGenerator The generator function that creates intermediary {@link SlotElement SlotElements}.
267+
* @return This {@link Builder Animation builder}
268+
*/
269+
Builder setIntermediaryElementGenerator(Function<? super Slot, ? extends @Nullable SlotElement> intermediaryGenerator);
221270

222271
/**
223272
* Adds a handler that is called when slot(s) are shown.
224273
*
225274
* @param showHandler The handler that is called when slot(s) are shown.
226275
* @return This {@link Builder Animation builder}
227276
*/
228-
Builder addShowHandler(BiConsumer<Animation, Set<Slot>> showHandler);
277+
Builder addShowHandler(BiConsumer<? super Animation, ? super Set<? extends Slot>> showHandler);
229278

230279
/**
231280
* Adds a handler that is called when the animation is finished.
232281
*
233282
* @param finishHandler The handler that is called when the animation is finished.
234283
* @return This {@link Builder Animation builder}
235284
*/
236-
Builder addFinishHandler(Consumer<Animation> finishHandler);
285+
Builder addFinishHandler(Consumer<? super Animation> finishHandler);
237286

238287
/**
239288
* Builds the animation.

invui/src/main/java/xyz/xenondevs/invui/gui/AnimationImpl.java

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717
final class AnimationImpl implements Animation {
1818

1919
private final int tickDelay;
20-
private final BiPredicate<Gui, Slot> slotFilter;
21-
private final Function<Animation, Set<Slot>> slotSelector;
22-
private BiConsumer<Animation, Set<Slot>> showHandler;
23-
private Consumer<Animation> finishHandler;
20+
private final BiPredicate<? super Gui, ? super Slot> slotFilter;
21+
private final Function<? super Animation, ? extends Set<? extends Slot>> slotSelector;
22+
private final Function<? super Slot, ? extends @Nullable SlotElement> intermediaryGenerator;
23+
private BiConsumer<? super Animation, Set<? extends Slot>> showHandler;
24+
private Consumer<? super Animation> finishHandler;
2425

2526
private final Set<Slot> remainingSlots = new HashSet<>();
2627
private @Nullable Gui gui;
@@ -30,14 +31,16 @@ final class AnimationImpl implements Animation {
3031

3132
public AnimationImpl(
3233
int tickDelay,
33-
BiPredicate<Gui, Slot> slotFilter,
34-
Function<Animation, Set<Slot>> slotSelector,
35-
BiConsumer<Animation, Set<Slot>> showHandler,
36-
Consumer<Animation> finishHandler
34+
BiPredicate<? super Gui, ? super Slot> slotFilter,
35+
Function<? super Animation, ? extends Set<? extends Slot>> slotSelector,
36+
Function<? super Slot, ? extends @Nullable SlotElement> intermediaryGenerator,
37+
BiConsumer<? super Animation, Set<? extends Slot>> showHandler,
38+
Consumer<? super Animation> finishHandler
3739
) {
3840
this.tickDelay = tickDelay;
3941
this.slotFilter = slotFilter;
4042
this.slotSelector = slotSelector;
43+
this.intermediaryGenerator = intermediaryGenerator;
4144
this.showHandler = showHandler;
4245
this.finishHandler = finishHandler;
4346
}
@@ -65,14 +68,18 @@ public boolean isFinished() {
6568
return remainingSlots.isEmpty();
6669
}
6770

68-
public void addShowHandler(Consumer<Set<Slot>> showHandler) {
71+
public void addShowHandler(Consumer<? super Set<? extends Slot>> showHandler) {
6972
this.showHandler = this.showHandler.andThen((animation, slot) -> showHandler.accept(slot));
7073
}
7174

7275
public void addFinishHandler(Runnable finishHandler) {
7376
this.finishHandler = this.finishHandler.andThen(animation -> finishHandler.run());
7477
}
7578

79+
public @Nullable SlotElement getIntermediarySlotElement(Slot slot) {
80+
return intermediaryGenerator.apply(slot);
81+
}
82+
7683
/**
7784
* Binds the animation to a gui and populates the remaining slots.
7885
*
@@ -135,9 +142,10 @@ static final class BuilderImpl implements Animation.Builder {
135142

136143
private int tickDelay = 1;
137144
private BiPredicate<Gui, Slot> slotFilter = (gui, slot) -> true;
138-
private BiConsumer<Animation, Set<Slot>> showHandler = (animation, slot) -> {};
145+
private @Nullable Function<? super Animation, ? extends Set<? extends Slot>> slotSelector;
146+
private Function<? super Slot, ? extends @Nullable SlotElement> intermediaryGenerator = slot -> null;
147+
private BiConsumer<Animation, Set<? extends Slot>> showHandler = (animation, slot) -> {};
139148
private Consumer<Animation> finishHandler = gui -> {};
140-
private @Nullable Function<Animation, Set<Slot>> slotSelector;
141149

142150
@Override
143151
public Animation.Builder setTickDelay(int tickDelay) {
@@ -146,25 +154,31 @@ public Animation.Builder setTickDelay(int tickDelay) {
146154
}
147155

148156
@Override
149-
public Animation.Builder setSlotSelector(Function<Animation, Set<Slot>> selector) {
157+
public Animation.Builder setSlotSelector(Function<? super Animation, ? extends Set<? extends Slot>> selector) {
150158
this.slotSelector = selector;
151159
return this;
152160
}
153161

154162
@Override
155-
public Animation.Builder addShowHandler(BiConsumer<Animation, Set<Slot>> showHandler) {
163+
public Builder setIntermediaryElementGenerator(Function<? super Slot, ? extends @Nullable SlotElement> intermediaryGenerator) {
164+
this.intermediaryGenerator = intermediaryGenerator;
165+
return this;
166+
}
167+
168+
@Override
169+
public Animation.Builder addShowHandler(BiConsumer<? super Animation, ? super Set<? extends Slot>> showHandler) {
156170
this.showHandler = this.showHandler.andThen(showHandler);
157171
return this;
158172
}
159173

160174
@Override
161-
public Animation.Builder addFinishHandler(Consumer<Animation> finishHandler) {
175+
public Animation.Builder addFinishHandler(Consumer<? super Animation> finishHandler) {
162176
this.finishHandler = this.finishHandler.andThen(finishHandler);
163177
return this;
164178
}
165179

166180
@Override
167-
public Animation.Builder addSlotFilter(BiPredicate<Gui, Slot> filter) {
181+
public Animation.Builder addSlotFilter(BiPredicate<? super Gui, ? super Slot> filter) {
168182
this.slotFilter = this.slotFilter.and(filter);
169183
return this;
170184
}
@@ -174,7 +188,7 @@ public Animation build() {
174188
if (slotSelector == null)
175189
throw new IllegalStateException("SlotSelector needs to be set");
176190

177-
return new AnimationImpl(tickDelay, slotFilter, slotSelector, showHandler, finishHandler);
191+
return new AnimationImpl(tickDelay, slotFilter, slotSelector, intermediaryGenerator, showHandler, finishHandler);
178192
}
179193

180194
}

invui/src/test/java/xyz/xenondevs/invui/gui/AnimationTest.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package xyz.xenondevs.invui.gui;
22

33
import org.bukkit.Material;
4+
import org.bukkit.entity.Player;
45
import org.bukkit.inventory.ItemStack;
56
import org.junit.jupiter.api.AfterAll;
67
import org.junit.jupiter.api.BeforeAll;
@@ -681,6 +682,54 @@ public void testAnimationCannotBePlayedTwice() {
681682
assertThrows(IllegalStateException.class, () -> gui2.playAnimation(rowAnimation));
682683
}
683684

685+
@Test
686+
public void testIntermediaryGenerator() {
687+
Player player = server.addPlayer();
688+
689+
Gui gui1 = createTestGui();
690+
Animation rowAnimation = Animation.row()
691+
.setIntermediaryElementGenerator(s -> new SlotElement.Item(Item.simple(ItemStack.of(Material.STONE, s.y() * 9 + s.x() + 1))))
692+
.build();
693+
694+
gui1.playAnimation(rowAnimation);
695+
696+
for (int i = 0; i < 9 * 4; i++) {
697+
SlotElement element = gui1.getSlotElement(i);
698+
assertNotNull(element, "i: " + i);
699+
ItemStack itemStack = element.getItemStack(player);
700+
assertNotNull(itemStack, "i: " + i);
701+
assertEquals(i + 1, itemStack.getAmount(), "i: " + i);
702+
}
703+
704+
server.getScheduler().performTicks(2);
705+
706+
for (int i = 0; i < 9 * 2; i++) {
707+
SlotElement element = gui1.getSlotElement(i);
708+
assertNotNull(element, "i: " + i);
709+
ItemStack itemStack = element.getItemStack(player);
710+
assertNotNull(itemStack, "i: " + i);
711+
assertEquals(Material.DIAMOND, itemStack.getType(), "i: " + i);
712+
}
713+
for (int i = 9 * 2; i < 9 * 4; i++) {
714+
SlotElement element = gui1.getSlotElement(i);
715+
assertNotNull(element, "i: " + i);
716+
ItemStack itemStack = element.getItemStack(player);
717+
assertNotNull(itemStack, "i: " + i);
718+
assertEquals(i + 1, itemStack.getAmount(), "i: " + i);
719+
}
720+
721+
server.getScheduler().performTicks(2);
722+
assertTrue(rowAnimation.isFinished());
723+
724+
for (int i = 0; i < 9 * 4; i++) {
725+
SlotElement element = gui1.getSlotElement(i);
726+
assertNotNull(element, "i: " + i);
727+
ItemStack itemStack = element.getItemStack(player);
728+
assertNotNull(itemStack, "i: " + i);
729+
assertEquals(Material.DIAMOND, itemStack.getType(), "i: " + i);
730+
}
731+
}
732+
684733
private Gui createOddTestGui() {
685734
Gui gui = Gui.empty(3, 1);
686735
gui.fill(Item.simple(new ItemStack(Material.DIAMOND)), true);

0 commit comments

Comments
 (0)