Skip to content

Commit

Permalink
Finish 'player equips armor' event, add 'player unequips armor'
Browse files Browse the repository at this point in the history
Yay! Denizen is now the only plugin (that I know of) that correctly
utilizes every necessary event to create a custom armor un/equip event!
Take that, Bukkit!
  • Loading branch information
Morphan1 committed Oct 19, 2014
1 parent 00bc297 commit bf37a3b
Show file tree
Hide file tree
Showing 3 changed files with 249 additions and 78 deletions.
Expand Up @@ -2,24 +2,26 @@

import net.aufdemrand.denizen.events.EventManager;
import net.aufdemrand.denizen.events.SmartEvent;
import net.aufdemrand.denizen.objects.dItem;
import net.aufdemrand.denizen.objects.dLocation;
import net.aufdemrand.denizen.objects.dObject;
import net.aufdemrand.denizen.objects.dPlayer;
import net.aufdemrand.denizen.objects.*;
import net.aufdemrand.denizen.utilities.DenizenAPI;
import net.aufdemrand.denizen.utilities.Utilities;
import net.aufdemrand.denizen.utilities.debugging.dB;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockDispenseEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;

import java.util.Arrays;
import java.util.HashMap;
Expand All @@ -43,12 +45,27 @@ public boolean shouldInitialize(Set<String> events) {
for (String event : events) {

// Use a regex pattern to narrow down matches
Matcher m = Pattern.compile("on player equips (m@|i@)?\\w+", Pattern.CASE_INSENSITIVE)
Matcher m = Pattern.compile("on player (un)?equips ((m@|i@)?\\w+)", Pattern.CASE_INSENSITIVE)
.matcher(event);

if (m.matches()) {
// TODO: Check if it's a valid armor material or item?
return true;
String string = m.group(2);
if (string.equalsIgnoreCase("armor")
|| string.equalsIgnoreCase("helmet")
|| string.equalsIgnoreCase("chestplate")
|| string.equalsIgnoreCase("leggings")
|| string.equalsIgnoreCase("boots"))
return true;
else if (dMaterial.matches(string)) {
dMaterial material = dMaterial.valueOf(string);
if (material != null)
return isArmor(material.getMaterial());
}
else if (dItem.matches(string)) {
dItem item = dItem.valueOf(string);
if (item != null)
return isArmor(item.getItemStack());
}
}
}
// No matches at all, just fail.
Expand All @@ -67,7 +84,8 @@ public void _initialize() {
@Override
public void breakDown() {
PlayerInteractEvent.getHandlerList().unregister(this);
InventoryCloseEvent.getHandlerList().unregister(this);
InventoryClickEvent.getHandlerList().unregister(this);
InventoryDragEvent.getHandlerList().unregister(this);
BlockDispenseEvent.getHandlerList().unregister(this);
}

Expand All @@ -78,92 +96,255 @@ public void breakDown() {

@EventHandler
public void blockDispense(BlockDispenseEvent event) {
dItem item = new dItem(event.getItem());
dLocation location = new dLocation(event.getBlock().getLocation());
final ItemStack item = event.getItem();
final Location location = event.getBlock().getLocation();

if (isArmor(item)) {
for (final Player player : location.getWorld().getPlayers()) {
if (Utilities.checkLocation(player, location, 2.5)) {
final ItemStack[] armor_contents = player.getInventory().getArmorContents();
final Vector velocity = event.getVelocity();
new BukkitRunnable() {
@Override
public void run() {
ItemStack[] new_armor = player.getInventory().getArmorContents();
for (int i = 0; i < new_armor.length; i++) {
ItemStack before = armor_contents[i];
ItemStack now = new_armor[i];
if (now != null && now.getType() == item.getType()
&& now.getDurability() == item.getDurability()
&& (before == null || before.getType() == Material.AIR)) {
if (playerEquipsArmorEvent(player, item)) {
player.getInventory().setContents(armor_contents);
location.getWorld().dropItemNaturally(location, item).setVelocity(velocity);
}
}
}
}
}.runTaskLater(DenizenAPI.getCurrentInstance(), 0); // Yes, 0 is correct.
}
}
}
}

if (item.isArmor()) {
for (Player player : location.getWorld().getPlayers())
if (Utilities.checkLocation(player, location, 1.5))
playerEquipsArmorEvent(player, event.getItem(), player.getInventory().firstEmpty());
@EventHandler
public void inventoryClick(InventoryClickEvent event) {
Inventory inventory = event.getInventory();
if (!didPlayerClickOwnInventory((Player) event.getWhoClicked(), inventory))
return;
ItemStack item = event.getCurrentItem();
Player player = (Player) inventory.getHolder();
ItemStack cursor = event.getCursor();
if (event.getSlotType() == InventoryType.SlotType.ARMOR) {
if (item != null && item.getType() != Material.AIR
&& (cursor == null || cursor.getType() == Material.AIR || isArmor(cursor))) {
if (playerUnequipsArmorEvent(player, item)) {
event.setCancelled(true);
return;
}
}
if (cursor != null && cursor.getType() != Material.AIR && isArmor(cursor)) {
if (playerEquipsArmorEvent(player, cursor)) {
event.setCancelled(true);
return;
}
}
}
else if (event.getClick().isShiftClick() && item != null && isArmor(item)) {
ItemStack currentItem = player.getInventory().getArmorContents()[getArmorTypeNumber(item)];
if (currentItem == null || currentItem.getType() == Material.AIR) {
if (playerEquipsArmorEvent(player, item)) {
event.setCancelled(true);
return;
}
}
}
}

@EventHandler
public void inventoryCloseEvent(InventoryCloseEvent event) {

// TODO: Handle clicks in-inventory instead of the close event?
if (event.getInventory().getHolder() instanceof Player) {
PlayerInventory inv = (PlayerInventory) event.getInventory().getHolder().getInventory();
ItemStack[] armor_contents = inv.getArmorContents();
for (int s = 0; s < 4; s++) {
if (armor_contents[0].getType() != Material.AIR)
playerEquipsArmorEvent((Player) inv.getHolder(), armor_contents[s], inv.firstEmpty());
public void inventoryDrag(InventoryDragEvent event) {
Inventory inventory = event.getInventory();
if (!didPlayerClickOwnInventory((Player) event.getWhoClicked(), inventory))
return;
ItemStack item = event.getOldCursor();
Player player = (Player) inventory.getHolder();
if (!isArmor(item)) return;
int[] armor_slots = new int[]{5,6,7,8};
Set<Integer> slots = event.getRawSlots();
for (int slot : armor_slots) {
if (slots.contains(slot) && (slot-5 == getArmorTypeNumber(item))) {
ItemStack before = inventory.getItem(slot);
if (before == null || before.getType() == Material.AIR) {
if (playerEquipsArmorEvent(player, item)) {
event.setCancelled(true);
return;
}
}
}
}
}

@EventHandler
public void playerInteract(PlayerInteractEvent event) {
if (event.hasItem()) {
dItem item = new dItem(event.getItem());
if ((event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) && item.isArmor()) {
playerEquipsArmorEvent(event.getPlayer(), event.getItem(), event.getPlayer().getInventory().getHeldItemSlot());
ItemStack item = event.getItem();
Action action = event.getAction();
if ((action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK)
|| !isArmor(item) || isInteractive(event.getClickedBlock().getType()))
return;
ItemStack currentItem = event.getPlayer().getInventory().getArmorContents()[getArmorTypeNumber(item)];
if (currentItem == null || currentItem.getType() == Material.AIR) {
if (playerEquipsArmorEvent(event.getPlayer(), item)) {
event.setCancelled(true);
return;
}
}
}
}

private boolean isArmor(ItemStack itemStack) {
int id = itemStack.getTypeId();
return id >= 298 && id <= 317;
}

private boolean isArmor(Material material) {
int id = material.getId();
return id >= 298 && id <= 317;
}

private boolean didPlayerClickOwnInventory(Player player, Inventory inventory) {
InventoryHolder holder = inventory.getHolder();
InventoryType type = inventory.getType();
return holder != null
&& holder.equals(player)
&& type != InventoryType.ENDER_CHEST
&& type != InventoryType.WORKBENCH;
}

private int getArmorTypeNumber(ItemStack itemStack) {
return (itemStack.getTypeId()-298)%4;
}

private String getArmorType(ItemStack itemStack) {
if (!isArmor(itemStack))
return "helmet";
switch (getArmorTypeNumber(itemStack)) {
case 0:
return "helmet";
case 1:
return "chestplate";
case 2:
return "leggings";
case 3:
return "boots";
}
return null;
}

private boolean isInteractive(Material material) {
if (material == null || !material.isBlock()) {
return false;
}
switch (material) {
case DISPENSER:
case NOTE_BLOCK:
case BED_BLOCK:
case CHEST:
case WORKBENCH:
case FURNACE:
case BURNING_FURNACE:
case WOODEN_DOOR:
case LEVER:
case REDSTONE_ORE:
case STONE_BUTTON:
case JUKEBOX:
case CAKE_BLOCK:
case DIODE_BLOCK_ON:
case DIODE_BLOCK_OFF:
case TRAP_DOOR:
case FENCE_GATE:
case ENCHANTMENT_TABLE:
case BREWING_STAND:
case DRAGON_EGG:
case ENDER_CHEST:
case COMMAND:
case BEACON:
case WOOD_BUTTON:
case ANVIL:
case TRAPPED_CHEST:
case REDSTONE_COMPARATOR_ON:
case REDSTONE_COMPARATOR_OFF:
case HOPPER:
case DROPPER:
return true;
default:
return false;
}
}

// <--[event]
// @Events
// player equips armor
// player equips <item>
// player equips [helmet/chestplate/leggings/boots]
//
// @Regex on player equips (m@|i@)?\w+
//
// @Warning This event is minimally tested and unstable.
//
// @Triggers when a player equips armor, or closes their inventory after equipping new armor.
// @Triggers when a player equips armor.
// @Context
// <context.armor> returns the dItem that was equipped.
//
// @Determine
// "CANCELLED" to stop the armor from being equipped.
//
// -->
public void playerEquipsArmorEvent(final Player player, final ItemStack item, final int replaceSlot) {

// Run this as a not-so-delayed Runnable...
// This is to force Bukkit to see any newly equipped armor
new BukkitRunnable() {
@Override
public void run() {
dItem armor = new dItem(item);
ItemStack[] armor_contents = player.getInventory().getArmorContents();

int type = 3-((item.getTypeId()-298)%4);
// TODO: Catch index error here
if (armor.comparesTo(armor_contents[type]) == -1) {
for (ItemStack item : player.getInventory().getArmorContents()) {
// TODO: Something here?
}
return;
}
private boolean playerEquipsArmorEvent(final Player player, final ItemStack item) {

dItem armor = new dItem(item);

Map<String, dObject> context = new HashMap<String, dObject>();
context.put("armor", armor);
Map<String, dObject> context = new HashMap<String, dObject>();
context.put("armor", armor);

String determination = EventManager.doEvents(Arrays.asList
String determination = EventManager.doEvents(Arrays.asList
("player equips armor",
"player equips " + getArmorType(item),
"player equips " + armor.identifySimple(),
"player equips " + armor.identifyMaterial()),
null, new dPlayer(player), context).toUpperCase();
null, new dPlayer(player), context).toUpperCase();

if (determination.startsWith("CANCELLED")) {
armor_contents[type] = new ItemStack(Material.AIR);
player.getInventory().setArmorContents(armor_contents);
player.getInventory().setItem(replaceSlot, item);
}
}
}.runTaskLater(DenizenAPI.getCurrentInstance(), 0); // TODO: is 0 a reasonable delay here?
return determination.startsWith("CANCELLED");

}

// <--[event]
// @Events
// player unequips armor
// player unequips <item>
// player unequips [helmet/chestplate/leggings/boots]
//
// @Regex on player unequips (m@|i@)?\w+
//
// @Triggers when a player unequips armor.
// @Context
// <context.armor> returns the dItem that was unequipped.
//
// @Determine
// "CANCELLED" to stop the armor from being unequipped.
// -->
private boolean playerUnequipsArmorEvent(final Player player, final ItemStack item) {

dItem armor = new dItem(item);

Map<String, dObject> context = new HashMap<String, dObject>();
context.put("armor", armor);

String determination = EventManager.doEvents(Arrays.asList
("player unequips armor",
"player unequips " + getArmorType(item),
"player unequips " + armor.identifySimple(),
"player unequips " + armor.identifyMaterial()),
null, new dPlayer(player), context).toUpperCase();

return determination.startsWith("CANCELLED");

}
}
10 changes: 0 additions & 10 deletions src/main/java/net/aufdemrand/denizen/objects/dItem.java
Expand Up @@ -370,16 +370,6 @@ public String getLore(String prefix) {
return "";
}

/**
* Check whether this item is some form of armor.
*
* @return True if it is, otherwise false.
*/
public boolean isArmor() {
int type = item.getTypeId();
return type >= 298 && type <= 317;
}

/**
* Check whether this item contains the lore specific
* to item scripts.
Expand Down

0 comments on commit bf37a3b

Please sign in to comment.