001 package net.minecraft.tileentity;
002
003 import java.util.Iterator;
004 import java.util.List;
005 import net.minecraft.block.Block;
006 import net.minecraft.entity.player.EntityPlayer;
007 import net.minecraft.inventory.ContainerChest;
008 import net.minecraft.inventory.IInventory;
009 import net.minecraft.inventory.InventoryLargeChest;
010 import net.minecraft.item.ItemStack;
011 import net.minecraft.nbt.NBTTagCompound;
012 import net.minecraft.nbt.NBTTagList;
013 import net.minecraft.util.AxisAlignedBB;
014
015 public class TileEntityChest extends TileEntity implements IInventory
016 {
017 private ItemStack[] chestContents = new ItemStack[36];
018
019 /** Determines if the check for adjacent chests has taken place. */
020 public boolean adjacentChestChecked = false;
021
022 /** Contains the chest tile located adjacent to this one (if any) */
023 public TileEntityChest adjacentChestZNeg;
024
025 /** Contains the chest tile located adjacent to this one (if any) */
026 public TileEntityChest adjacentChestXPos;
027
028 /** Contains the chest tile located adjacent to this one (if any) */
029 public TileEntityChest adjacentChestXNeg;
030
031 /** Contains the chest tile located adjacent to this one (if any) */
032 public TileEntityChest adjacentChestZPosition;
033
034 /** The current angle of the lid (between 0 and 1) */
035 public float lidAngle;
036
037 /** The angle of the lid last tick */
038 public float prevLidAngle;
039
040 /** The number of players currently using this chest */
041 public int numUsingPlayers;
042
043 /** Server sync counter (once per 20 ticks) */
044 private int ticksSinceSync;
045
046 /**
047 * Returns the number of slots in the inventory.
048 */
049 public int getSizeInventory()
050 {
051 return 27;
052 }
053
054 /**
055 * Returns the stack in slot i
056 */
057 public ItemStack getStackInSlot(int par1)
058 {
059 return this.chestContents[par1];
060 }
061
062 /**
063 * Removes from an inventory slot (first arg) up to a specified number (second arg) of items and returns them in a
064 * new stack.
065 */
066 public ItemStack decrStackSize(int par1, int par2)
067 {
068 if (this.chestContents[par1] != null)
069 {
070 ItemStack var3;
071
072 if (this.chestContents[par1].stackSize <= par2)
073 {
074 var3 = this.chestContents[par1];
075 this.chestContents[par1] = null;
076 this.onInventoryChanged();
077 return var3;
078 }
079 else
080 {
081 var3 = this.chestContents[par1].splitStack(par2);
082
083 if (this.chestContents[par1].stackSize == 0)
084 {
085 this.chestContents[par1] = null;
086 }
087
088 this.onInventoryChanged();
089 return var3;
090 }
091 }
092 else
093 {
094 return null;
095 }
096 }
097
098 /**
099 * When some containers are closed they call this on each slot, then drop whatever it returns as an EntityItem -
100 * like when you close a workbench GUI.
101 */
102 public ItemStack getStackInSlotOnClosing(int par1)
103 {
104 if (this.chestContents[par1] != null)
105 {
106 ItemStack var2 = this.chestContents[par1];
107 this.chestContents[par1] = null;
108 return var2;
109 }
110 else
111 {
112 return null;
113 }
114 }
115
116 /**
117 * Sets the given item stack to the specified slot in the inventory (can be crafting or armor sections).
118 */
119 public void setInventorySlotContents(int par1, ItemStack par2ItemStack)
120 {
121 this.chestContents[par1] = par2ItemStack;
122
123 if (par2ItemStack != null && par2ItemStack.stackSize > this.getInventoryStackLimit())
124 {
125 par2ItemStack.stackSize = this.getInventoryStackLimit();
126 }
127
128 this.onInventoryChanged();
129 }
130
131 /**
132 * Returns the name of the inventory.
133 */
134 public String getInvName()
135 {
136 return "container.chest";
137 }
138
139 /**
140 * Reads a tile entity from NBT.
141 */
142 public void readFromNBT(NBTTagCompound par1NBTTagCompound)
143 {
144 super.readFromNBT(par1NBTTagCompound);
145 NBTTagList var2 = par1NBTTagCompound.getTagList("Items");
146 this.chestContents = new ItemStack[this.getSizeInventory()];
147
148 for (int var3 = 0; var3 < var2.tagCount(); ++var3)
149 {
150 NBTTagCompound var4 = (NBTTagCompound)var2.tagAt(var3);
151 int var5 = var4.getByte("Slot") & 255;
152
153 if (var5 >= 0 && var5 < this.chestContents.length)
154 {
155 this.chestContents[var5] = ItemStack.loadItemStackFromNBT(var4);
156 }
157 }
158 }
159
160 /**
161 * Writes a tile entity to NBT.
162 */
163 public void writeToNBT(NBTTagCompound par1NBTTagCompound)
164 {
165 super.writeToNBT(par1NBTTagCompound);
166 NBTTagList var2 = new NBTTagList();
167
168 for (int var3 = 0; var3 < this.chestContents.length; ++var3)
169 {
170 if (this.chestContents[var3] != null)
171 {
172 NBTTagCompound var4 = new NBTTagCompound();
173 var4.setByte("Slot", (byte)var3);
174 this.chestContents[var3].writeToNBT(var4);
175 var2.appendTag(var4);
176 }
177 }
178
179 par1NBTTagCompound.setTag("Items", var2);
180 }
181
182 /**
183 * Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. *Isn't
184 * this more of a set than a get?*
185 */
186 public int getInventoryStackLimit()
187 {
188 return 64;
189 }
190
191 /**
192 * Do not make give this method the name canInteractWith because it clashes with Container
193 */
194 public boolean isUseableByPlayer(EntityPlayer par1EntityPlayer)
195 {
196 return this.worldObj.getBlockTileEntity(this.xCoord, this.yCoord, this.zCoord) != this ? false : par1EntityPlayer.getDistanceSq((double)this.xCoord + 0.5D, (double)this.yCoord + 0.5D, (double)this.zCoord + 0.5D) <= 64.0D;
197 }
198
199 /**
200 * Causes the TileEntity to reset all it's cached values for it's container block, blockID, metaData and in the case
201 * of chests, the adjcacent chest check
202 */
203 public void updateContainingBlockInfo()
204 {
205 super.updateContainingBlockInfo();
206 this.adjacentChestChecked = false;
207 }
208
209 private void func_90009_a(TileEntityChest par1TileEntityChest, int par2)
210 {
211 if (par1TileEntityChest.isInvalid())
212 {
213 this.adjacentChestChecked = false;
214 }
215 else if (this.adjacentChestChecked)
216 {
217 switch (par2)
218 {
219 case 0:
220 if (this.adjacentChestZPosition != par1TileEntityChest)
221 {
222 this.adjacentChestChecked = false;
223 }
224
225 break;
226 case 1:
227 if (this.adjacentChestXNeg != par1TileEntityChest)
228 {
229 this.adjacentChestChecked = false;
230 }
231
232 break;
233 case 2:
234 if (this.adjacentChestZNeg != par1TileEntityChest)
235 {
236 this.adjacentChestChecked = false;
237 }
238
239 break;
240 case 3:
241 if (this.adjacentChestXPos != par1TileEntityChest)
242 {
243 this.adjacentChestChecked = false;
244 }
245 }
246 }
247 }
248
249 /**
250 * Performs the check for adjacent chests to determine if this chest is double or not.
251 */
252 public void checkForAdjacentChests()
253 {
254 if (!this.adjacentChestChecked)
255 {
256 this.adjacentChestChecked = true;
257 this.adjacentChestZNeg = null;
258 this.adjacentChestXPos = null;
259 this.adjacentChestXNeg = null;
260 this.adjacentChestZPosition = null;
261
262 if (this.worldObj.getBlockId(this.xCoord - 1, this.yCoord, this.zCoord) == Block.chest.blockID)
263 {
264 this.adjacentChestXNeg = (TileEntityChest)this.worldObj.getBlockTileEntity(this.xCoord - 1, this.yCoord, this.zCoord);
265 }
266
267 if (this.worldObj.getBlockId(this.xCoord + 1, this.yCoord, this.zCoord) == Block.chest.blockID)
268 {
269 this.adjacentChestXPos = (TileEntityChest)this.worldObj.getBlockTileEntity(this.xCoord + 1, this.yCoord, this.zCoord);
270 }
271
272 if (this.worldObj.getBlockId(this.xCoord, this.yCoord, this.zCoord - 1) == Block.chest.blockID)
273 {
274 this.adjacentChestZNeg = (TileEntityChest)this.worldObj.getBlockTileEntity(this.xCoord, this.yCoord, this.zCoord - 1);
275 }
276
277 if (this.worldObj.getBlockId(this.xCoord, this.yCoord, this.zCoord + 1) == Block.chest.blockID)
278 {
279 this.adjacentChestZPosition = (TileEntityChest)this.worldObj.getBlockTileEntity(this.xCoord, this.yCoord, this.zCoord + 1);
280 }
281
282 if (this.adjacentChestZNeg != null)
283 {
284 this.adjacentChestZNeg.func_90009_a(this, 0);
285 }
286
287 if (this.adjacentChestZPosition != null)
288 {
289 this.adjacentChestZPosition.func_90009_a(this, 2);
290 }
291
292 if (this.adjacentChestXPos != null)
293 {
294 this.adjacentChestXPos.func_90009_a(this, 1);
295 }
296
297 if (this.adjacentChestXNeg != null)
298 {
299 this.adjacentChestXNeg.func_90009_a(this, 3);
300 }
301 }
302 }
303
304 /**
305 * Allows the entity to update its state. Overridden in most subclasses, e.g. the mob spawner uses this to count
306 * ticks and creates a new spawn inside its implementation.
307 */
308 public void updateEntity()
309 {
310 super.updateEntity();
311 this.checkForAdjacentChests();
312 ++this.ticksSinceSync;
313 float var1;
314
315 if (!this.worldObj.isRemote && this.numUsingPlayers != 0 && (this.ticksSinceSync + this.xCoord + this.yCoord + this.zCoord) % 200 == 0)
316 {
317 this.numUsingPlayers = 0;
318 var1 = 5.0F;
319 List var2 = this.worldObj.getEntitiesWithinAABB(EntityPlayer.class, AxisAlignedBB.getAABBPool().addOrModifyAABBInPool((double)((float)this.xCoord - var1), (double)((float)this.yCoord - var1), (double)((float)this.zCoord - var1), (double)((float)(this.xCoord + 1) + var1), (double)((float)(this.yCoord + 1) + var1), (double)((float)(this.zCoord + 1) + var1)));
320 Iterator var3 = var2.iterator();
321
322 while (var3.hasNext())
323 {
324 EntityPlayer var4 = (EntityPlayer)var3.next();
325
326 if (var4.openContainer instanceof ContainerChest)
327 {
328 IInventory var5 = ((ContainerChest)var4.openContainer).getLowerChestInventory();
329
330 if (var5 == this || var5 instanceof InventoryLargeChest && ((InventoryLargeChest)var5).isPartOfLargeChest(this))
331 {
332 ++this.numUsingPlayers;
333 }
334 }
335 }
336 }
337
338 this.prevLidAngle = this.lidAngle;
339 var1 = 0.1F;
340 double var11;
341
342 if (this.numUsingPlayers > 0 && this.lidAngle == 0.0F && this.adjacentChestZNeg == null && this.adjacentChestXNeg == null)
343 {
344 double var8 = (double)this.xCoord + 0.5D;
345 var11 = (double)this.zCoord + 0.5D;
346
347 if (this.adjacentChestZPosition != null)
348 {
349 var11 += 0.5D;
350 }
351
352 if (this.adjacentChestXPos != null)
353 {
354 var8 += 0.5D;
355 }
356
357 this.worldObj.playSoundEffect(var8, (double)this.yCoord + 0.5D, var11, "random.chestopen", 0.5F, this.worldObj.rand.nextFloat() * 0.1F + 0.9F);
358 }
359
360 if (this.numUsingPlayers == 0 && this.lidAngle > 0.0F || this.numUsingPlayers > 0 && this.lidAngle < 1.0F)
361 {
362 float var9 = this.lidAngle;
363
364 if (this.numUsingPlayers > 0)
365 {
366 this.lidAngle += var1;
367 }
368 else
369 {
370 this.lidAngle -= var1;
371 }
372
373 if (this.lidAngle > 1.0F)
374 {
375 this.lidAngle = 1.0F;
376 }
377
378 float var10 = 0.5F;
379
380 if (this.lidAngle < var10 && var9 >= var10 && this.adjacentChestZNeg == null && this.adjacentChestXNeg == null)
381 {
382 var11 = (double)this.xCoord + 0.5D;
383 double var6 = (double)this.zCoord + 0.5D;
384
385 if (this.adjacentChestZPosition != null)
386 {
387 var6 += 0.5D;
388 }
389
390 if (this.adjacentChestXPos != null)
391 {
392 var11 += 0.5D;
393 }
394
395 this.worldObj.playSoundEffect(var11, (double)this.yCoord + 0.5D, var6, "random.chestclosed", 0.5F, this.worldObj.rand.nextFloat() * 0.1F + 0.9F);
396 }
397
398 if (this.lidAngle < 0.0F)
399 {
400 this.lidAngle = 0.0F;
401 }
402 }
403 }
404
405 /**
406 * Called when a client event is received with the event number and argument, see World.sendClientEvent
407 */
408 public void receiveClientEvent(int par1, int par2)
409 {
410 if (par1 == 1)
411 {
412 this.numUsingPlayers = par2;
413 }
414 }
415
416 public void openChest()
417 {
418 ++this.numUsingPlayers;
419 this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, Block.chest.blockID, 1, this.numUsingPlayers);
420 }
421
422 public void closeChest()
423 {
424 --this.numUsingPlayers;
425 this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, Block.chest.blockID, 1, this.numUsingPlayers);
426 }
427
428 /**
429 * invalidates a tile entity
430 */
431 public void invalidate()
432 {
433 super.invalidate();
434 this.updateContainingBlockInfo();
435 this.checkForAdjacentChests();
436 }
437 }