001package net.minecraft.entity.item; 002 003import java.util.Iterator; 004 005import net.minecraftforge.common.MinecraftForge; 006import net.minecraftforge.event.Event.Result; 007import net.minecraftforge.event.entity.item.ItemExpireEvent; 008import net.minecraftforge.event.entity.player.EntityItemPickupEvent; 009 010import cpw.mods.fml.common.registry.GameRegistry; 011import net.minecraft.block.Block; 012import net.minecraft.block.material.Material; 013import net.minecraft.entity.Entity; 014import net.minecraft.entity.player.EntityPlayer; 015import net.minecraft.item.Item; 016import net.minecraft.item.ItemStack; 017import net.minecraft.nbt.NBTTagCompound; 018import net.minecraft.stats.AchievementList; 019import net.minecraft.util.DamageSource; 020import net.minecraft.util.MathHelper; 021import net.minecraft.util.StatCollector; 022import net.minecraft.world.World; 023 024public class EntityItem extends Entity 025{ 026 /** 027 * The age of this EntityItem (used to animate it up and down as well as expire it) 028 */ 029 public int age; 030 public int delayBeforeCanPickup; 031 032 /** The health of this EntityItem. (For example, damage for tools) */ 033 private int health; 034 035 /** The EntityItem's random initial float height. */ 036 public float hoverStart; 037 038 /** 039 * The maximum age of this EntityItem. The item is expired once this is reached. 040 */ 041 public int lifespan = 6000; 042 043 public EntityItem(World par1World, double par2, double par4, double par6) 044 { 045 super(par1World); 046 this.age = 0; 047 this.health = 5; 048 this.hoverStart = (float)(Math.random() * Math.PI * 2.0D); 049 this.setSize(0.25F, 0.25F); 050 this.yOffset = this.height / 2.0F; 051 this.setPosition(par2, par4, par6); 052 this.rotationYaw = (float)(Math.random() * 360.0D); 053 this.motionX = (double)((float)(Math.random() * 0.20000000298023224D - 0.10000000149011612D)); 054 this.motionY = 0.20000000298023224D; 055 this.motionZ = (double)((float)(Math.random() * 0.20000000298023224D - 0.10000000149011612D)); 056 } 057 058 public EntityItem(World par1World, double par2, double par4, double par6, ItemStack par8ItemStack) 059 { 060 this(par1World, par2, par4, par6); 061 this.setEntityItemStack(par8ItemStack); 062 this.lifespan = (par8ItemStack.getItem() == null ? 6000 : par8ItemStack.getItem().getEntityLifespan(par8ItemStack, par1World)); 063 } 064 065 /** 066 * returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to 067 * prevent them from trampling crops 068 */ 069 protected boolean canTriggerWalking() 070 { 071 return false; 072 } 073 074 public EntityItem(World par1World) 075 { 076 super(par1World); 077 this.age = 0; 078 this.health = 5; 079 this.hoverStart = (float)(Math.random() * Math.PI * 2.0D); 080 this.setSize(0.25F, 0.25F); 081 this.yOffset = this.height / 2.0F; 082 } 083 084 protected void entityInit() 085 { 086 this.getDataWatcher().addObjectByDataType(10, 5); 087 } 088 089 /** 090 * Called to update the entity's position/logic. 091 */ 092 public void onUpdate() 093 { 094 ItemStack stack = this.getDataWatcher().getWatchableObjectItemStack(10); 095 if (stack != null && stack.getItem() != null) 096 { 097 if (stack.getItem().onEntityItemUpdate(this)) 098 { 099 return; 100 } 101 } 102 103 super.onUpdate(); 104 105 if (this.delayBeforeCanPickup > 0) 106 { 107 --this.delayBeforeCanPickup; 108 } 109 110 this.prevPosX = this.posX; 111 this.prevPosY = this.posY; 112 this.prevPosZ = this.posZ; 113 this.motionY -= 0.03999999910593033D; 114 this.noClip = this.pushOutOfBlocks(this.posX, (this.boundingBox.minY + this.boundingBox.maxY) / 2.0D, this.posZ); 115 this.moveEntity(this.motionX, this.motionY, this.motionZ); 116 boolean flag = (int)this.prevPosX != (int)this.posX || (int)this.prevPosY != (int)this.posY || (int)this.prevPosZ != (int)this.posZ; 117 118 if (flag || this.ticksExisted % 25 == 0) 119 { 120 if (this.worldObj.getBlockMaterial(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY), MathHelper.floor_double(this.posZ)) == Material.lava) 121 { 122 this.motionY = 0.20000000298023224D; 123 this.motionX = (double)((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F); 124 this.motionZ = (double)((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F); 125 this.playSound("random.fizz", 0.4F, 2.0F + this.rand.nextFloat() * 0.4F); 126 } 127 128 if (!this.worldObj.isRemote) 129 { 130 this.searchForOtherItemsNearby(); 131 } 132 } 133 134 float f = 0.98F; 135 136 if (this.onGround) 137 { 138 f = 0.58800006F; 139 int i = this.worldObj.getBlockId(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.boundingBox.minY) - 1, MathHelper.floor_double(this.posZ)); 140 141 if (i > 0) 142 { 143 f = Block.blocksList[i].slipperiness * 0.98F; 144 } 145 } 146 147 this.motionX *= (double)f; 148 this.motionY *= 0.9800000190734863D; 149 this.motionZ *= (double)f; 150 151 if (this.onGround) 152 { 153 this.motionY *= -0.5D; 154 } 155 156 ++this.age; 157 158 ItemStack item = getDataWatcher().getWatchableObjectItemStack(10); 159 160 if (!this.worldObj.isRemote && this.age >= lifespan) 161 { 162 if (item != null) 163 { 164 ItemExpireEvent event = new ItemExpireEvent(this, (item.getItem() == null ? 6000 : item.getItem().getEntityLifespan(item, worldObj))); 165 if (MinecraftForge.EVENT_BUS.post(event)) 166 { 167 lifespan += event.extraLife; 168 } 169 else 170 { 171 this.setDead(); 172 } 173 } 174 else 175 { 176 this.setDead(); 177 } 178 } 179 180 if (item != null && item.stackSize <= 0) 181 { 182 this.setDead(); 183 } 184 } 185 186 /** 187 * Looks for other itemstacks nearby and tries to stack them together 188 */ 189 private void searchForOtherItemsNearby() 190 { 191 Iterator iterator = this.worldObj.getEntitiesWithinAABB(EntityItem.class, this.boundingBox.expand(0.5D, 0.0D, 0.5D)).iterator(); 192 193 while (iterator.hasNext()) 194 { 195 EntityItem entityitem = (EntityItem)iterator.next(); 196 this.combineItems(entityitem); 197 } 198 } 199 200 /** 201 * Tries to merge this item with the item passed as the parameter. Returns true if successful. Either this item or 202 * the other item will be removed from the world. 203 */ 204 public boolean combineItems(EntityItem par1EntityItem) 205 { 206 if (par1EntityItem == this) 207 { 208 return false; 209 } 210 else if (par1EntityItem.isEntityAlive() && this.isEntityAlive()) 211 { 212 ItemStack itemstack = this.getEntityItem(); 213 ItemStack itemstack1 = par1EntityItem.getEntityItem(); 214 215 if (itemstack1.getItem() != itemstack.getItem()) 216 { 217 return false; 218 } 219 else if (itemstack1.hasTagCompound() ^ itemstack.hasTagCompound()) 220 { 221 return false; 222 } 223 else if (itemstack1.hasTagCompound() && !itemstack1.getTagCompound().equals(itemstack.getTagCompound())) 224 { 225 return false; 226 } 227 else if (itemstack1.getItem().getHasSubtypes() && itemstack1.getItemDamage() != itemstack.getItemDamage()) 228 { 229 return false; 230 } 231 else if (itemstack1.stackSize < itemstack.stackSize) 232 { 233 return par1EntityItem.combineItems(this); 234 } 235 else if (itemstack1.stackSize + itemstack.stackSize > itemstack1.getMaxStackSize()) 236 { 237 return false; 238 } 239 else 240 { 241 itemstack1.stackSize += itemstack.stackSize; 242 par1EntityItem.delayBeforeCanPickup = Math.max(par1EntityItem.delayBeforeCanPickup, this.delayBeforeCanPickup); 243 par1EntityItem.age = Math.min(par1EntityItem.age, this.age); 244 par1EntityItem.setEntityItemStack(itemstack1); 245 this.setDead(); 246 return true; 247 } 248 } 249 else 250 { 251 return false; 252 } 253 } 254 255 /** 256 * sets the age of the item so that it'll despawn one minute after it has been dropped (instead of five). Used when 257 * items are dropped from players in creative mode 258 */ 259 public void setAgeToCreativeDespawnTime() 260 { 261 this.age = 4800; 262 } 263 264 /** 265 * Returns if this entity is in water and will end up adding the waters velocity to the entity 266 */ 267 public boolean handleWaterMovement() 268 { 269 return this.worldObj.handleMaterialAcceleration(this.boundingBox, Material.water, this); 270 } 271 272 /** 273 * Will deal the specified amount of damage to the entity if the entity isn't immune to fire damage. Args: 274 * amountDamage 275 */ 276 protected void dealFireDamage(int par1) 277 { 278 this.attackEntityFrom(DamageSource.inFire, par1); 279 } 280 281 /** 282 * Called when the entity is attacked. 283 */ 284 public boolean attackEntityFrom(DamageSource par1DamageSource, int par2) 285 { 286 if (this.isEntityInvulnerable()) 287 { 288 return false; 289 } 290 else if (this.getEntityItem() != null && this.getEntityItem().itemID == Item.netherStar.itemID && par1DamageSource.isExplosion()) 291 { 292 return false; 293 } 294 else 295 { 296 this.setBeenAttacked(); 297 this.health -= par2; 298 299 if (this.health <= 0) 300 { 301 this.setDead(); 302 } 303 304 return false; 305 } 306 } 307 308 /** 309 * (abstract) Protected helper method to write subclass entity data to NBT. 310 */ 311 public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound) 312 { 313 par1NBTTagCompound.setShort("Health", (short)((byte)this.health)); 314 par1NBTTagCompound.setShort("Age", (short)this.age); 315 par1NBTTagCompound.setInteger("Lifespan", lifespan); 316 317 if (this.getEntityItem() != null) 318 { 319 par1NBTTagCompound.setCompoundTag("Item", this.getEntityItem().writeToNBT(new NBTTagCompound())); 320 } 321 } 322 323 /** 324 * (abstract) Protected helper method to read subclass entity data from NBT. 325 */ 326 public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound) 327 { 328 this.health = par1NBTTagCompound.getShort("Health") & 255; 329 this.age = par1NBTTagCompound.getShort("Age"); 330 NBTTagCompound nbttagcompound1 = par1NBTTagCompound.getCompoundTag("Item"); 331 this.setEntityItemStack(ItemStack.loadItemStackFromNBT(nbttagcompound1)); 332 333 ItemStack item = getDataWatcher().getWatchableObjectItemStack(10); 334 335 if (item == null || item.stackSize <= 0) 336 { 337 this.setDead(); 338 } 339 340 if (par1NBTTagCompound.hasKey("Lifespan")) 341 { 342 lifespan = par1NBTTagCompound.getInteger("Lifespan"); 343 } 344 } 345 346 /** 347 * Called by a player entity when they collide with an entity 348 */ 349 public void onCollideWithPlayer(EntityPlayer par1EntityPlayer) 350 { 351 if (!this.worldObj.isRemote) 352 { 353 if (this.delayBeforeCanPickup > 0) 354 { 355 return; 356 } 357 358 EntityItemPickupEvent event = new EntityItemPickupEvent(par1EntityPlayer, this); 359 360 if (MinecraftForge.EVENT_BUS.post(event)) 361 { 362 return; 363 } 364 365 ItemStack itemstack = this.getEntityItem(); 366 int i = itemstack.stackSize; 367 368 if (this.delayBeforeCanPickup <= 0 && (event.getResult() == Result.ALLOW || i <= 0 || par1EntityPlayer.inventory.addItemStackToInventory(itemstack))) 369 { 370 if (itemstack.itemID == Block.wood.blockID) 371 { 372 par1EntityPlayer.triggerAchievement(AchievementList.mineWood); 373 } 374 375 if (itemstack.itemID == Item.leather.itemID) 376 { 377 par1EntityPlayer.triggerAchievement(AchievementList.killCow); 378 } 379 380 if (itemstack.itemID == Item.diamond.itemID) 381 { 382 par1EntityPlayer.triggerAchievement(AchievementList.diamonds); 383 } 384 385 if (itemstack.itemID == Item.blazeRod.itemID) 386 { 387 par1EntityPlayer.triggerAchievement(AchievementList.blazeRod); 388 } 389 390 GameRegistry.onPickupNotification(par1EntityPlayer, this); 391 392 this.playSound("random.pop", 0.2F, ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.7F + 1.0F) * 2.0F); 393 par1EntityPlayer.onItemPickup(this, i); 394 395 if (itemstack.stackSize <= 0) 396 { 397 this.setDead(); 398 } 399 } 400 } 401 } 402 403 /** 404 * Gets the username of the entity. 405 */ 406 public String getEntityName() 407 { 408 return StatCollector.translateToLocal("item." + this.getEntityItem().getItemName()); 409 } 410 411 /** 412 * If returns false, the item will not inflict any damage against entities. 413 */ 414 public boolean canAttackWithItem() 415 { 416 return false; 417 } 418 419 /** 420 * Teleports the entity to another dimension. Params: Dimension number to teleport to 421 */ 422 public void travelToDimension(int par1) 423 { 424 super.travelToDimension(par1); 425 426 if (!this.worldObj.isRemote) 427 { 428 this.searchForOtherItemsNearby(); 429 } 430 } 431 432 /** 433 * Returns the ItemStack corresponding to the Entity (Note: if no item exists, will log an error but still return an 434 * ItemStack containing Block.stone) 435 */ 436 public ItemStack getEntityItem() 437 { 438 ItemStack itemstack = this.getDataWatcher().getWatchableObjectItemStack(10); 439 440 if (itemstack == null) 441 { 442 if (this.worldObj != null) 443 { 444 this.worldObj.getWorldLogAgent().logSevere("Item entity " + this.entityId + " has no item?!"); 445 } 446 447 return new ItemStack(Block.stone); 448 } 449 else 450 { 451 return itemstack; 452 } 453 } 454 455 /** 456 * Sets the ItemStack for this entity 457 */ 458 public void setEntityItemStack(ItemStack par1ItemStack) 459 { 460 this.getDataWatcher().updateObject(10, par1ItemStack); 461 this.getDataWatcher().setObjectWatched(10); 462 } 463}