001    package org.bukkit.inventory;
002    
003    import org.bukkit.GameMode;
004    import org.bukkit.entity.HumanEntity;
005    import org.bukkit.event.inventory.InventoryType;
006    
007    /**
008     * Represents a view linking two inventories and a single player (whose
009     * inventory may or may not be one of the two).
010     * <p>
011     * Note: If you implement this interface but fail to satisfy the expected
012     * contracts of certain methods, there's no guarantee that the game will work
013     * as it should.
014     */
015    public abstract class InventoryView {
016        public final static int OUTSIDE = -999;
017        /**
018         * Represents various extra properties of certain inventory windows.
019         */
020        public enum Property {
021            /**
022             * The progress of the down-pointing arrow in a brewing inventory.
023             */
024            BREW_TIME(0, InventoryType.BREWING),
025            /**
026             * The progress of the right-pointing arrow in a furnace inventory.
027             */
028            COOK_TIME(0, InventoryType.FURNACE),
029            /**
030             * The progress of the flame in a furnace inventory.
031             */
032            BURN_TIME(1, InventoryType.FURNACE),
033            /**
034             * How many total ticks the current fuel should last.
035             */
036            TICKS_FOR_CURRENT_FUEL(2, InventoryType.FURNACE),
037            /**
038             * In an enchanting inventory, the top button's experience level
039             * value.
040             */
041            ENCHANT_BUTTON1(0, InventoryType.ENCHANTING),
042            /**
043             * In an enchanting inventory, the middle button's experience level
044             * value.
045             */
046            ENCHANT_BUTTON2(1, InventoryType.ENCHANTING),
047            /**
048             * In an enchanting inventory, the bottom button's experience level
049             * value.
050             */
051            ENCHANT_BUTTON3(2, InventoryType.ENCHANTING);
052            int id;
053            InventoryType style;
054            private Property(int id, InventoryType appliesTo) {
055                this.id = id;
056                style = appliesTo;
057            }
058    
059            public InventoryType getType() {
060                return style;
061            }
062    
063            /**
064             *
065             * @deprecated Magic value
066             */
067            @Deprecated
068            public int getId() {
069                return id;
070            }
071        }
072        /**
073         * Get the upper inventory involved in this transaction.
074         *
075         * @return the inventory
076         */
077        public abstract Inventory getTopInventory();
078    
079        /**
080         * Get the lower inventory involved in this transaction.
081         *
082         * @return the inventory
083         */
084        public abstract Inventory getBottomInventory();
085    
086        /**
087         * Get the player viewing.
088         *
089         * @return the player
090         */
091        public abstract HumanEntity getPlayer();
092    
093        /**
094         * Determine the type of inventory involved in the transaction. This
095         * indicates the window style being shown. It will never return PLAYER,
096         * since that is common to all windows.
097         *
098         * @return the inventory type
099         */
100        public abstract InventoryType getType();
101    
102        /**
103         * Sets one item in this inventory view by its raw slot ID.
104         * <p>
105         * Note: If slot ID -999 is chosen, it may be expected that the item is
106         * dropped on the ground. This is not required behaviour, however.
107         *
108         * @param slot The ID as returned by InventoryClickEvent.getRawSlot()
109         * @param item The new item to put in the slot, or null to clear it.
110         */
111        public void setItem(int slot, ItemStack item) {
112            if (slot != OUTSIDE) {
113                if (slot < getTopInventory().getSize()) {
114                    getTopInventory().setItem(convertSlot(slot),item);
115                } else {
116                    getBottomInventory().setItem(convertSlot(slot),item);
117                }
118            } else {
119                getPlayer().getWorld().dropItemNaturally(getPlayer().getLocation(), item);
120            }
121        }
122    
123        /**
124         * Gets one item in this inventory view by its raw slot ID.
125         *
126         * @param slot The ID as returned by InventoryClickEvent.getRawSlot()
127         * @return The item currently in the slot.
128         */
129        public ItemStack getItem(int slot) {
130            if (slot == OUTSIDE) {
131                return null;
132            }
133            if (slot < getTopInventory().getSize()) {
134                return getTopInventory().getItem(convertSlot(slot));
135            } else {
136                return getBottomInventory().getItem(convertSlot(slot));
137            }
138        }
139    
140        /**
141         * Sets the item on the cursor of one of the viewing players.
142         *
143         * @param item The item to put on the cursor, or null to remove the item
144         *     on their cursor.
145         */
146        public final void setCursor(ItemStack item) {
147            getPlayer().setItemOnCursor(item);
148        }
149    
150        /**
151         * Get the item on the cursor of one of the viewing players.
152         *
153         * @return The item on the player's cursor, or null if they aren't holding
154         *     one.
155         */
156        public final ItemStack getCursor() {
157            return getPlayer().getItemOnCursor();
158        }
159    
160        /**
161         * Converts a raw slot ID into its local slot ID into whichever of the two
162         * inventories the slot points to.
163         * <p>
164         * If the raw slot refers to the upper inventory, it will be returned
165         * unchanged and thus be suitable for getTopInventory().getItem(); if it
166         * refers to the lower inventory, the output will differ from the input
167         * and be suitable for getBottomInventory().getItem().
168         *
169         * @param rawSlot The raw slot ID.
170         * @return The converted slot ID.
171         */
172        public final int convertSlot(int rawSlot) {
173            int numInTop = getTopInventory().getSize();
174            if (rawSlot < numInTop) {
175                return rawSlot;
176            }
177            int slot = rawSlot - numInTop;
178            if (getPlayer().getGameMode() == GameMode.CREATIVE && getType() == InventoryType.PLAYER) {
179                return slot;
180            }
181            if (getType() == InventoryType.CRAFTING) {
182                if(slot < 4) return 39 - slot;
183                else slot -= 4;
184            }
185            if (slot >= 27) slot -= 27;
186            else slot += 9;
187            return slot;
188        }
189    
190        /**
191         * Closes the inventory view.
192         */
193        public final void close() {
194            getPlayer().closeInventory();
195        }
196    
197        /**
198         * Check the total number of slots in this view, combining the upper and
199         * lower inventories.
200         * <p>
201         * Note though that it's possible for this to be greater than the sum of
202         * the two inventories if for example some slots are not being used.
203         *
204         * @return The total size
205         */
206        public final int countSlots() {
207            return getTopInventory().getSize() + getBottomInventory().getSize();
208        }
209    
210        /**
211         * Sets an extra property of this inventory if supported by that
212         * inventory, for example the state of a progress bar.
213         *
214         * @param prop the window property to update
215         * @param value the new value for the window property
216         * @return true if the property was updated successfully, false if the
217         *     property is not supported by that inventory
218         */
219        public final boolean setProperty(Property prop, int value) {
220            return getPlayer().setWindowProperty(prop, value);
221        }
222    
223        /**
224         * Get the title of this inventory window.
225         *
226         * @return The title.
227         */
228        public final String getTitle() {
229            return getTopInventory().getTitle();
230        }
231    }