package net.glowstone.inventory;
import net.glowstone.GlowServer;
import net.glowstone.entity.GlowHumanEntity;
import net.glowstone.entity.GlowPlayer;
import net.glowstone.inventory.crafting.CraftingManager;
import net.glowstone.net.message.play.inv.HeldItemMessage;
import net.glowstone.util.InventoryUtil;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.inventory.InventoryType.SlotType;
import org.bukkit.inventory.*;
import org.bukkit.material.MaterialData;
/**
* An Inventory representing the items a player is holding.
*/
public class GlowPlayerInventory extends GlowInventory implements PlayerInventory, EntityEquipment {
private static final int SIZE = InventoryType.PLAYER.getDefaultSize();
/*
* Armor slots
*/
private static final int BOOTS_SLOT = 36;
private static final int LEGGINGS_SLOT = 37;
private static final int CHESTPLATE_SLOT = 38;
private static final int HELMET_SLOT = 39;
/*
* Off hand slot
*/
private static final int OFF_HAND_SLOT = 40;
/**
* The crafting inventory.
*/
private final GlowCraftingInventory crafting;
/**
* Tracker for inventory drags.
*/
private final DragTracker tracker = new DragTracker();
/**
* The current held item slot.
*/
private int heldSlot;
/**
* The human entity for this inventory, stored for location.
*/
private GlowHumanEntity owner;
public GlowPlayerInventory(GlowHumanEntity owner) {
// all player inventories are ID 0
// 36 = 4 rows of 9
// + 4 = armor, completed inventory
// + 1 = off hand slot
super(owner, InventoryType.PLAYER, SIZE);
crafting = new GlowCraftingInventory(owner, InventoryType.CRAFTING);
this.owner = owner;
for (int i = 0; i <= 8; i++) {
getSlot(i).setType(SlotType.QUICKBAR);
}
for (int i = BOOTS_SLOT; i <= HELMET_SLOT; i++) {
getSlot(i).setType(SlotType.ARMOR);
}
}
////////////////////////////////////////////////////////////////////////////
// Internals
public static boolean canEquipInHelmetSlot(Material material) {
return EnchantmentTarget.ARMOR_HEAD.includes(material) || material == Material.PUMPKIN || material == Material.SKULL_ITEM;
}
/**
* Get the crafting inventory.
*
* @return The GlowCraftingInventory attached to this player
*/
public GlowCraftingInventory getCraftingInventory() {
return crafting;
}
public void setRawHeldItemSlot(int slot) {
if (slot < 0 || slot > 8) {
throw new IllegalArgumentException(slot + " not in range 0..8");
}
heldSlot = slot;
setItemInMainHand(getItemInMainHand()); // send to player again just in case
}
@Override
public boolean itemPlaceAllowed(int slot, ItemStack stack) {
if (slot == BOOTS_SLOT) {
return EnchantmentTarget.ARMOR_FEET.includes(stack);
}
if (slot == LEGGINGS_SLOT) {
return EnchantmentTarget.ARMOR_LEGS.includes(stack);
}
if (slot == CHESTPLATE_SLOT) {
return EnchantmentTarget.ARMOR_TORSO.includes(stack);
}
if (slot == HELMET_SLOT) {
return canEquipInHelmetSlot(stack.getType());
}
return super.itemPlaceAllowed(slot, stack);
}
@Override
public void handleShiftClick(GlowPlayer player, InventoryView view, int clickedSlot, ItemStack clickedItem) {
GlowInventory top = (GlowInventory) view.getTopInventory();
// If this is the default inventory try to equip the item as armor first
if (GlowInventoryView.isDefault(view)) {
clickedItem = tryToFillSlots(clickedItem, 36, 40);
}
// Check whether the top inventory allows shift clicking in any of it's slots
boolean topAllowsShiftClick = false;
for (int i = 0; i < top.getSize(); i++) {
if (top.itemShiftClickAllowed(i, clickedItem)) {
topAllowsShiftClick = true;
break;
}
}
if (topAllowsShiftClick) {
if (top.getType().equals(InventoryType.FURNACE)) {
CraftingManager cm = ((GlowServer) Bukkit.getServer()).getCraftingManager();
if (cm.getFurnaceRecipe(clickedItem) != null) {
// move items are be burnable to the input slot TODO: Use of variable (INPUT_SLOT) instead of hard coded value ?
clickedItem = top.tryToFillSlots(clickedItem, 0, -1);
} else if (cm.isFuel(clickedItem.getType())) {
// move fuel items direct to fuel slot TODO: Use of variable (FUEL_SLOT) instead of hard coded value ?
clickedItem = top.tryToFillSlots(clickedItem, 1, -1);
} else {
// switch them between hotbar and main inventory depending on where they are now
if (view.convertSlot(clickedSlot) < 9 || view.convertSlot(clickedSlot) >= 36) {
// move from hotbar and armor to main inventory
clickedItem = tryToFillSlots(clickedItem, 9, 36);
} else {
// move from main inventory to hotbar
clickedItem = tryToFillSlots(clickedItem, 0, 9);
}
}
} else {
// move items to the top inventory
clickedItem = top.tryToFillSlots(clickedItem, 0, top.getSize());
}
} else {
// switch them between hotbar and main inventory depending on where they are now
if (view.convertSlot(clickedSlot) < 9 || view.convertSlot(clickedSlot) >= 36) {
// move from hotbar and armor to main inventory
clickedItem = tryToFillSlots(clickedItem, 9, 36);
} else {
// move from main inventory to hotbar
clickedItem = tryToFillSlots(clickedItem, 0, 9);
}
}
view.setItem(clickedSlot, clickedItem);
}
////////////////////////////////////////////////////////////////////////////
// Overrides
/**
* Get the DragTracker associated with this player.
*
* @return The DragTracker.
*/
public DragTracker getDragTracker() {
return tracker;
}
@Override
public HumanEntity getHolder() {
return (HumanEntity) super.getHolder();
}
@Override
public ItemStack getItem(EquipmentSlot slot) {
switch (slot) {
case HAND:
return getItemInMainHand();
case OFF_HAND:
return getItemInOffHand();
case FEET:
return getBoots();
case LEGS:
return getLeggings();
case CHEST:
return getChestplate();
case HEAD:
return getHelmet();
default:
return null;
}
}
@Override
public void setItem(EquipmentSlot slot, ItemStack item) {
switch (slot) {
case HAND:
setItemInMainHand(item);
break;
case OFF_HAND:
setItemInOffHand(item);
break;
case FEET:
setBoots(item);
break;
case LEGS:
setLeggings(item);
break;
case CHEST:
setChestplate(item);
break;
case HEAD:
setHelmet(item);
break;
}
}
////////////////////////////////////////////////////////////////////////////
// Interface implementation
@Override
public ItemStack[] getStorageContents() {
ItemStack[] storage = new ItemStack[36];
for (int i = 0; i < 36; i++) {
storage[i] = getItem(i);
}
return storage;
}
@Override
public void setStorageContents(ItemStack[] items) throws IllegalArgumentException {
if (items.length != 36) {
throw new IllegalArgumentException("Length of player storage must be 36");
}
for (int i = 0; i < 36; i++) {
setItem(i, items[i]);
}
}
@Override
public ItemStack[] getArmorContents() {
ItemStack[] armor = new ItemStack[4];
for (int i = 0; i < 4; i++) {
armor[i] = getItem(BOOTS_SLOT + i);
}
return armor;
}
@Override
public void setArmorContents(ItemStack[] items) {
if (items.length != 4) {
throw new IllegalArgumentException("Length of armor must be 4");
}
for (int i = 0; i < 4; i++) {
setItem(BOOTS_SLOT + i, items[i]);
}
}
@Override
public ItemStack[] getExtraContents() {
return new ItemStack[]{getItemInOffHand()};
}
@Override
public void setExtraContents(ItemStack[] items) {
if (items.length != 1) {
throw new IllegalArgumentException("Length of extra contents must be 1");
}
setItemInOffHand(items[0]);
}
@Override
public ItemStack getHelmet() {
return getItem(HELMET_SLOT);
}
@Override
public void setHelmet(ItemStack helmet) {
setItem(HELMET_SLOT, helmet);
}
@Override
public ItemStack getChestplate() {
return getItem(CHESTPLATE_SLOT);
}
@Override
public void setChestplate(ItemStack chestplate) {
setItem(CHESTPLATE_SLOT, chestplate);
}
@Override
public ItemStack getLeggings() {
return getItem(LEGGINGS_SLOT);
}
@Override
public void setLeggings(ItemStack leggings) {
setItem(LEGGINGS_SLOT, leggings);
}
@Override
public ItemStack getBoots() {
return getItem(BOOTS_SLOT);
}
@Override
public void setBoots(ItemStack boots) {
setItem(BOOTS_SLOT, boots);
}
@Override
public ItemStack getItemInMainHand() {
return getItem(heldSlot).clone();
}
@Override
public void setItemInMainHand(ItemStack item) {
setItem(heldSlot, item);
}
@Override
public ItemStack getItemInOffHand() {
return getItem(OFF_HAND_SLOT).clone();
}
@Override
public void setItemInOffHand(ItemStack item) {
setItem(OFF_HAND_SLOT, item);
}
@Override
@Deprecated
public ItemStack getItemInHand() {
return getItemInMainHand();
}
@Override
@Deprecated
public void setItemInHand(ItemStack item) {
setItemInMainHand(item);
}
@Override
public int getHeldItemSlot() {
return heldSlot;
}
@Override
public void setHeldItemSlot(int slot) {
setRawHeldItemSlot(slot);
if (getHolder() instanceof GlowPlayer) {
((GlowPlayer) getHolder()).getSession().send(new HeldItemMessage(slot));
}
}
@Override
public Location getLocation() {
return owner.getLocation();
}
public int clear(Material type, MaterialData data) {
int numCleared = 0;
for (int i = 0; i < getSize(); ++i) {
ItemStack stack = getItem(i);
if (stack != null && (type == null || stack.getType() == type) && (data == null || stack.getData().equals(data))) {
setItem(i, InventoryUtil.createEmptyStack());
if (!InventoryUtil.isEmpty(stack)) {
// never report AIR as removed - else will report all empty slots cleared
numCleared += stack.getAmount(); // report # items, not # stacks removed
}
}
}
return numCleared;
}
@Override
@Deprecated
public int clear(int id, int data) {
int numCleared = 0;
for (int i = 0; i < getSize(); ++i) {
ItemStack stack = getItem(i);
if (stack != null && (id == -1 || stack.getTypeId() == id) && (data == -1 || stack.getData().getData() == data)) {
setItem(i, InventoryUtil.createEmptyStack());
if (!InventoryUtil.isEmpty(stack)) {
// never report AIR as removed - else will report all empty slots cleared
numCleared += stack.getAmount(); // report # items, not # stacks removed
}
}
}
return numCleared;
}
@Override
public float getItemInHandDropChance() {
return getItemInMainHandDropChance();
}
@Override
public void setItemInHandDropChance(float chance) {
setItemInMainHandDropChance(chance);
}
@Override
public float getItemInMainHandDropChance() {
return 1;
}
@Override
public void setItemInMainHandDropChance(float chance) {
throw new UnsupportedOperationException();
}
@Override
public float getItemInOffHandDropChance() {
return 1;
}
@Override
public void setItemInOffHandDropChance(float chance) {
throw new UnsupportedOperationException();
}
@Override
public float getHelmetDropChance() {
return 1;
}
@Override
public void setHelmetDropChance(float chance) {
throw new UnsupportedOperationException();
}
@Override
public float getChestplateDropChance() {
return 1;
}
@Override
public void setChestplateDropChance(float chance) {
throw new UnsupportedOperationException();
}
@Override
public float getLeggingsDropChance() {
return 1;
}
@Override
public void setLeggingsDropChance(float chance) {
throw new UnsupportedOperationException();
}
@Override
public float getBootsDropChance() {
return 1;
}
@Override
public void setBootsDropChance(float chance) {
throw new UnsupportedOperationException();
}
}