diff --git a/src/main/java/net/citizensnpcs/api/ai/speech/SimpleSpeechController.java b/src/main/java/net/citizensnpcs/api/ai/speech/SimpleSpeechController.java index 9c22ac38..c04b14fe 100644 --- a/src/main/java/net/citizensnpcs/api/ai/speech/SimpleSpeechController.java +++ b/src/main/java/net/citizensnpcs/api/ai/speech/SimpleSpeechController.java @@ -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; @@ -32,5 +32,4 @@ public void speak(SpeechContext context, String vocalChordName) { return; CitizensAPI.getSpeechFactory().getVocalChord(event.getVocalChordName()).talk(context); } - } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/api/gui/InventoryMenu.java b/src/main/java/net/citizensnpcs/api/gui/InventoryMenu.java index 9617a12c..68b96797 100644 --- a/src/main/java/net/citizensnpcs/api/gui/InventoryMenu.java +++ b/src/main/java/net/citizensnpcs/api/gui/InventoryMenu.java @@ -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 stack = Queues.newArrayDeque(); - private final Collection views = Lists.newArrayList(); + private Collection 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); @@ -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) { @@ -89,9 +116,9 @@ private void transition(InventoryMenuInfo info) { } else { inventory = Bukkit.createInventory(null, info.menuAnnotation.type(), info.menuAnnotation.title()); } + List 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) { @@ -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 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 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 patternInfo = info.patterns[i]; - InventoryMenuPattern pat = new InventoryMenuPattern(page.ctx, patternInfo.data); - patternInfo.bind(pat); + Bindable patternInfo = info.patterns[i]; + Collection patternSlots = Lists.newArrayList(); + Collection 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 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 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)); } } @@ -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(); } @@ -144,12 +191,12 @@ public void bind(Object instance) { private static class InventoryMenuInfo { Constructor constructor; Menu menuAnnotation; - Bindable[] patterns; + Bindable[] patterns; Bindable[] slots; Bindable[] 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); } @@ -160,6 +207,8 @@ private Bindable[] getBindables(Class clazz, Class< List> 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) { @@ -179,6 +228,8 @@ private Bindable[] 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(null, t)); } @@ -189,9 +240,88 @@ private Bindable[] getBindables(Class clazz, Class< return bindables.toArray(new Bindable[bindables.size()]); } + private Bindable getPatternBindable(MethodHandle bind, AccessibleObject object) { + MenuPattern[] annotation = object.getAnnotationsByType(MenuPattern.class); + MenuPattern pattern = annotation[0]; + Collection slots = Lists.newArrayList(); + for (MenuSlot slot : object.getAnnotationsByType(MenuSlot.class)) { + if (pattern.value().contains(Character.toString(slot.pat()))) { + slots.add(slot); + } + } + Collection transitions = Lists.newArrayList(); + for (MenuTransition transition : object.getAnnotationsByType(MenuTransition.class)) { + if (pattern.value().contains(Character.toString(transition.pat()))) { + transitions.add(transition); + } + } + return new Bindable(bind, new MenuPatternInfo(pattern, slots, transitions)); + } + + private Bindable getPatternBindable(MethodHandle bind, Class object) { + MenuPattern[] annotation = object.getAnnotationsByType(MenuPattern.class); + if (annotation.length != 1) + return null; + MenuPattern pattern = annotation[0]; + Collection slots = Lists.newArrayList(); + for (MenuSlot slot : object.getAnnotationsByType(MenuSlot.class)) { + if (pattern.value().contains(Character.toString(slot.pat()))) { + slots.add(slot); + } + } + Collection transitions = Lists.newArrayList(); + for (MenuTransition transition : object.getAnnotationsByType(MenuTransition.class)) { + if (pattern.value().contains(Character.toString(transition.pat()))) { + transitions.add(transition); + } + } + return new Bindable(bind, new MenuPatternInfo(pattern, slots, transitions)); + } + + @SuppressWarnings({ "unchecked" }) + private Bindable[] getPatternBindables(Class clazz) { + Collection> 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 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 slots = Lists.newArrayList(); + Collection transitions = Lists.newArrayList(); + + public MenuPatternInfo(MenuPattern info, Collection slots, Collection transitions) { + this.info = info; + this.slots = slots; + this.transitions = transitions; + } + } + private static class PageContext { private MenuContext ctx; private InventoryMenuPage page; diff --git a/src/main/java/net/citizensnpcs/api/gui/InventoryMenuPattern.java b/src/main/java/net/citizensnpcs/api/gui/InventoryMenuPattern.java index 509aa7a4..e34fa767 100644 --- a/src/main/java/net/citizensnpcs/api/gui/InventoryMenuPattern.java +++ b/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 slots; + private final Collection transitions; + + public InventoryMenuPattern(MenuPattern info, Collection slots, + Collection transitions) { + this.info = info; + this.slots = slots; + this.transitions = transitions; } } diff --git a/src/main/java/net/citizensnpcs/api/gui/InventoryMenuSlot.java b/src/main/java/net/citizensnpcs/api/gui/InventoryMenuSlot.java index d69ba3ae..c3f3aba4 100644 --- a/src/main/java/net/citizensnpcs/api/gui/InventoryMenuSlot.java +++ b/src/main/java/net/citizensnpcs/api/gui/InventoryMenuSlot.java @@ -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 filter) { diff --git a/src/main/java/net/citizensnpcs/api/gui/MenuPattern.java b/src/main/java/net/citizensnpcs/api/gui/MenuPattern.java index dcda1915..5dbb06cb 100644 --- a/src/main/java/net/citizensnpcs/api/gui/MenuPattern.java +++ b/src/main/java/net/citizensnpcs/api/gui/MenuPattern.java @@ -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 */ diff --git a/src/main/java/net/citizensnpcs/api/gui/MenuSlot.java b/src/main/java/net/citizensnpcs/api/gui/MenuSlot.java index 4ffc4890..83d2c45d 100644 --- a/src/main/java/net/citizensnpcs/api/gui/MenuSlot.java +++ b/src/main/java/net/citizensnpcs/api/gui/MenuSlot.java @@ -29,6 +29,11 @@ */ Material material() default Material.AIR; + /** + * For use with patterns. + */ + char pat(); + /** * The position of the slot within the inventory. */ diff --git a/src/main/java/net/citizensnpcs/api/gui/MenuTransition.java b/src/main/java/net/citizensnpcs/api/gui/MenuTransition.java index d3139cdd..997da94a 100644 --- a/src/main/java/net/citizensnpcs/api/gui/MenuTransition.java +++ b/src/main/java/net/citizensnpcs/api/gui/MenuTransition.java @@ -19,6 +19,11 @@ */ ClickType[] filter() default {}; + /** + * For use with patterns. + */ + char pat(); + /** * The position of the slot within the inventory. */