Skip to content

Commit

Permalink
More work on menu guis
Browse files Browse the repository at this point in the history
  • Loading branch information
fullwall committed Jan 25, 2021
1 parent 933e7b0 commit f199a05
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 38 deletions.
Expand Up @@ -9,7 +9,7 @@

/**
* Simple implementation of {@link SpeechController} which allows a NPC to speak with any registered {@link VocalChord}.
*
*
*/
public class SimpleSpeechController implements SpeechController {
NPC npc;
Expand All @@ -32,5 +32,4 @@ public void speak(SpeechContext context, String vocalChordName) {
return;
CitizensAPI.getSpeechFactory().getVocalChord(event.getVocalChordName()).talk(context);
}

}
174 changes: 152 additions & 22 deletions src/main/java/net/citizensnpcs/api/gui/InventoryMenu.java
Expand Up @@ -17,29 +17,55 @@
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;

import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.collect.Queues;

public class InventoryMenu implements Listener {
private PageContext page;
private final Queue<PageContext> stack = Queues.newArrayDeque();
private final Collection<InventoryView> views = Lists.newArrayList();
private Collection<InventoryView> views = Lists.newArrayList();

public InventoryMenu(InventoryMenuInfo info) {
transition(info);
}

private InventoryMenuSlot createSlot(int[] dim, MenuSlot slotInfo) {
int pos = posToIndex(dim, slotInfo.value());
InventoryMenuSlot slot = page.ctx.getSlot(pos);
slot.initialise(slotInfo);
return slot;
}

private InventoryMenuTransition createTransition(int[] dim, MenuTransition transitionInfo) {
int pos = posToIndex(dim, transitionInfo.pos());
InventoryMenuSlot slot = page.ctx.getSlot(pos);
InventoryMenuTransition transition = new InventoryMenuTransition(this, slot, transitionInfo.value());
return transition;
}

@EventHandler(ignoreCancelled = true)
public void onInventoryClick(InventoryClickEvent event) {
if (!event.getInventory().equals(page.ctx.getInventory()) || event.getAction() == InventoryAction.NOTHING)
if (!event.getInventory().equals(page.ctx.getInventory()))
return;
switch (event.getAction()) {
case COLLECT_TO_CURSOR:
event.setCancelled(true);
case NOTHING:
case UNKNOWN:
case DROP_ONE_CURSOR:
case DROP_ALL_CURSOR:
return;
default:
break;
}
InventoryMenuSlot slot = page.ctx.getSlot(event.getSlot());
page.page.onClick(slot, event);
slot.onClick(event);
Expand All @@ -58,6 +84,7 @@ public void onInventoryClose(InventoryCloseEvent event) {
return;
page.page.onClose(event.getPlayer());
page = stack.poll();
transitionViewersToInventory(page.ctx.getInventory());
}

private int posToIndex(int[] dim, int[] pos) {
Expand Down Expand Up @@ -89,9 +116,9 @@ private void transition(InventoryMenuInfo info) {
} else {
inventory = Bukkit.createInventory(null, info.menuAnnotation.type(), info.menuAnnotation.title());
}
List<InventoryMenuTransition> transitions = Lists.newArrayList();
InventoryMenuSlot[] slots = new InventoryMenuSlot[inventory.getSize()];
page.patterns = new InventoryMenuPattern[info.patterns.length];
page.transitions = new InventoryMenuTransition[info.transitions.length];
try {
page.page = info.constructor.newInstance();
} catch (Exception e) {
Expand All @@ -100,24 +127,44 @@ private void transition(InventoryMenuInfo info) {
page.ctx = new MenuContext(this, slots, inventory);
for (int i = 0; i < info.slots.length; i++) {
Bindable<MenuSlot> slotInfo = info.slots[i];
int pos = posToIndex(dim, slotInfo.data.value());
InventoryMenuSlot slot = page.ctx.getSlot(pos);
slot.initialise(slotInfo.data);
slotInfo.bind(slot);
InventoryMenuSlot slot = createSlot(dim, slotInfo.data);
slotInfo.bind(page.page, slot);
}
for (int i = 0; i < info.transitions.length; i++) {
Bindable<MenuTransition> transitionInfo = info.transitions[i];
InventoryMenuTransition transition = createTransition(dim, transitionInfo.data);
transitionInfo.bind(page.page, transition);
transitions.add(transition);
}
for (int i = 0; i < info.patterns.length; i++) {
Bindable<MenuPattern> patternInfo = info.patterns[i];
InventoryMenuPattern pat = new InventoryMenuPattern(page.ctx, patternInfo.data);
patternInfo.bind(pat);
Bindable<MenuPatternInfo> patternInfo = info.patterns[i];
Collection<InventoryMenuSlot> patternSlots = Lists.newArrayList();
Collection<InventoryMenuTransition> patternTransitions = Lists.newArrayList();
for (MenuSlot slot : patternInfo.data.slots) {
patternSlots.add(createSlot(dim, slot));
}
for (MenuTransition transition : patternInfo.data.transitions) {
InventoryMenuTransition concreteTransition = createTransition(dim, transition);
patternTransitions.add(concreteTransition);
transitions.add(concreteTransition);
}
InventoryMenuPattern pat = new InventoryMenuPattern(patternInfo.data.info, patternSlots,
patternTransitions);
patternInfo.bind(page.page, pat);
page.patterns[i] = pat;
}
for (int i = 0; i < info.transitions.length; i++) {
Bindable<MenuTransition> transitionInfo = info.transitions[i];
int pos = posToIndex(dim, transitionInfo.data.pos());
InventoryMenuSlot slot = page.ctx.getSlot(pos);
InventoryMenuTransition transition = new InventoryMenuTransition(this, slot, transitionInfo.data.value());
transitionInfo.bind(transition);
page.transitions[i] = transition;
page.transitions = transitions.toArray(new InventoryMenuTransition[transitions.size()]);
transitionViewersToInventory(inventory);
}

private void transitionViewersToInventory(Inventory inventory) {
Collection<InventoryView> old = views;
views = Lists.newArrayListWithExpectedSize(old.size());
for (InventoryView view : old) {
view.close();
if (!view.getPlayer().isValid() || inventory == null)
continue;
views.add(view.getPlayer().openInventory(inventory));
}
}

Expand All @@ -130,11 +177,11 @@ public Bindable(MethodHandle bind, T data) {
this.data = data;
}

public void bind(Object instance) {
public void bind(Object instance, Object value) {
if (bind == null)
return;
try {
bind.invoke(instance);
bind.invoke(instance, value);
} catch (Throwable e) {
e.printStackTrace();
}
Expand All @@ -144,12 +191,12 @@ public void bind(Object instance) {
private static class InventoryMenuInfo {
Constructor<? extends InventoryMenuPage> constructor;
Menu menuAnnotation;
Bindable<MenuPattern>[] patterns;
Bindable<MenuPatternInfo>[] patterns;
Bindable<MenuSlot>[] slots;
Bindable<MenuTransition>[] transitions;

public InventoryMenuInfo(Class<?> clazz) {
patterns = getBindables(clazz, MenuPattern.class, InventoryMenuPattern.class);
patterns = getPatternBindables(clazz);
slots = getBindables(clazz, MenuSlot.class, InventoryMenuSlot.class);
transitions = getBindables(clazz, MenuTransition.class, InventoryMenuTransition.class);
}
Expand All @@ -160,6 +207,8 @@ private <T extends Annotation> Bindable<T>[] getBindables(Class<?> clazz, Class<
List<Bindable<T>> bindables = Lists.newArrayList();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
if (field.getAnnotationsByType(MenuPattern.class).length != 0)
continue;
T[] annotations = field.getAnnotationsByType(annotationType);
MethodHandle bind = null;
if (field.getType() == concreteType) {
Expand All @@ -179,6 +228,8 @@ private <T extends Annotation> Bindable<T>[] getBindables(Class<?> clazz, Class<
reflect.addAll(Arrays.asList(clazz.getDeclaredMethods()));
for (AccessibleObject object : reflect) {
object.setAccessible(true);
if (object.getAnnotationsByType(MenuPattern.class).length != 0)
continue;
for (T t : object.getAnnotationsByType(annotationType)) {
bindables.add(new Bindable<T>(null, t));
}
Expand All @@ -189,9 +240,88 @@ private <T extends Annotation> Bindable<T>[] getBindables(Class<?> clazz, Class<
return bindables.toArray(new Bindable[bindables.size()]);
}

private Bindable<MenuPatternInfo> getPatternBindable(MethodHandle bind, AccessibleObject object) {
MenuPattern[] annotation = object.getAnnotationsByType(MenuPattern.class);
MenuPattern pattern = annotation[0];
Collection<MenuSlot> slots = Lists.newArrayList();
for (MenuSlot slot : object.getAnnotationsByType(MenuSlot.class)) {
if (pattern.value().contains(Character.toString(slot.pat()))) {
slots.add(slot);
}
}
Collection<MenuTransition> transitions = Lists.newArrayList();
for (MenuTransition transition : object.getAnnotationsByType(MenuTransition.class)) {
if (pattern.value().contains(Character.toString(transition.pat()))) {
transitions.add(transition);
}
}
return new Bindable<MenuPatternInfo>(bind, new MenuPatternInfo(pattern, slots, transitions));
}

private Bindable<MenuPatternInfo> getPatternBindable(MethodHandle bind, Class<?> object) {
MenuPattern[] annotation = object.getAnnotationsByType(MenuPattern.class);
if (annotation.length != 1)
return null;
MenuPattern pattern = annotation[0];
Collection<MenuSlot> slots = Lists.newArrayList();
for (MenuSlot slot : object.getAnnotationsByType(MenuSlot.class)) {
if (pattern.value().contains(Character.toString(slot.pat()))) {
slots.add(slot);
}
}
Collection<MenuTransition> transitions = Lists.newArrayList();
for (MenuTransition transition : object.getAnnotationsByType(MenuTransition.class)) {
if (pattern.value().contains(Character.toString(transition.pat()))) {
transitions.add(transition);
}
}
return new Bindable<MenuPatternInfo>(bind, new MenuPatternInfo(pattern, slots, transitions));
}

@SuppressWarnings({ "unchecked" })
private Bindable<MenuPatternInfo>[] getPatternBindables(Class<?> clazz) {
Collection<Bindable<MenuPatternInfo>> bindables = Lists.newArrayList();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
MethodHandle bind = null;
if (field.getType() == InventoryMenuPattern.class) {
try {
bind = LOOKUP.unreflectSetter(field);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
bindables.add(getPatternBindable(bind, field));
}

List<AccessibleObject> reflect = Lists.newArrayList();
reflect.addAll(Arrays.asList(clazz.getDeclaredConstructors()));
reflect.addAll(Arrays.asList(clazz.getDeclaredMethods()));
for (AccessibleObject object : reflect) {
object.setAccessible(true);
if (object.getAnnotationsByType(MenuPattern.class).length != 0)
continue;
bindables.add(getPatternBindable(null, object));
}
bindables.add(getPatternBindable(null, clazz));
return Collections2.filter(bindables, Predicates.notNull()).toArray(new Bindable[bindables.size()]);
}

private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
}

private static class MenuPatternInfo {
MenuPattern info;
Collection<MenuSlot> slots = Lists.newArrayList();
Collection<MenuTransition> transitions = Lists.newArrayList();

public MenuPatternInfo(MenuPattern info, Collection<MenuSlot> slots, Collection<MenuTransition> transitions) {
this.info = info;
this.slots = slots;
this.transitions = transitions;
}
}

private static class PageContext {
private MenuContext ctx;
private InventoryMenuPage page;
Expand Down
12 changes: 11 additions & 1 deletion src/main/java/net/citizensnpcs/api/gui/InventoryMenuPattern.java
@@ -1,6 +1,16 @@
package net.citizensnpcs.api.gui;

import java.util.Collection;

public class InventoryMenuPattern {
public InventoryMenuPattern(SlotSource source, MenuPattern init) {
private final MenuPattern info;
private final Collection<InventoryMenuSlot> slots;
private final Collection<InventoryMenuTransition> transitions;

public InventoryMenuPattern(MenuPattern info, Collection<InventoryMenuSlot> slots,
Collection<InventoryMenuTransition> transitions) {
this.info = info;
this.slots = slots;
this.transitions = transitions;
}
}
Expand Up @@ -58,7 +58,9 @@ public void initialise(MenuSlot data) {
}

public void onClick(InventoryClickEvent event) {
// TODO
if (!clickFilter.contains(event.getClick())) {
event.setCancelled(true);
}
}

public void setClickFilter(Collection<ClickType> filter) {
Expand Down
12 changes: 0 additions & 12 deletions src/main/java/net/citizensnpcs/api/gui/MenuPattern.java
Expand Up @@ -5,24 +5,12 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.bukkit.Material;

/**
* Defines a pattern of slots. Can be linked to a {@link InventoryMenuPattern} or simply at the class level.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.FIELD })
public @interface MenuPattern {
/**
* The amount of the itemstacks to display.
*/
int amount() default 1;

/**
* The material to display (defaults to AIR).
*/
Material material() default Material.AIR;

/**
* The pattern string. 0 = AIR
*/
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/net/citizensnpcs/api/gui/MenuSlot.java
Expand Up @@ -29,6 +29,11 @@
*/
Material material() default Material.AIR;

/**
* For use with patterns.
*/
char pat();

/**
* The position of the slot within the inventory.
*/
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/net/citizensnpcs/api/gui/MenuTransition.java
Expand Up @@ -19,6 +19,11 @@
*/
ClickType[] filter() default {};

/**
* For use with patterns.
*/
char pat();

/**
* The position of the slot within the inventory.
*/
Expand Down

0 comments on commit f199a05

Please sign in to comment.