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 super.onUpdate(); 095 096 if (this.delayBeforeCanPickup > 0) 097 { 098 --this.delayBeforeCanPickup; 099 } 100 101 this.prevPosX = this.posX; 102 this.prevPosY = this.posY; 103 this.prevPosZ = this.posZ; 104 this.motionY -= 0.03999999910593033D; 105 this.noClip = this.pushOutOfBlocks(this.posX, (this.boundingBox.minY + this.boundingBox.maxY) / 2.0D, this.posZ); 106 this.moveEntity(this.motionX, this.motionY, this.motionZ); 107 boolean flag = (int)this.prevPosX != (int)this.posX || (int)this.prevPosY != (int)this.posY || (int)this.prevPosZ != (int)this.posZ; 108 109 if (flag || this.ticksExisted % 25 == 0) 110 { 111 if (this.worldObj.getBlockMaterial(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY), MathHelper.floor_double(this.posZ)) == Material.lava) 112 { 113 this.motionY = 0.20000000298023224D; 114 this.motionX = (double)((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F); 115 this.motionZ = (double)((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F); 116 this.playSound("random.fizz", 0.4F, 2.0F + this.rand.nextFloat() * 0.4F); 117 } 118 119 if (!this.worldObj.isRemote) 120 { 121 this.searchForOtherItemsNearby(); 122 } 123 } 124 125 float f = 0.98F; 126 127 if (this.onGround) 128 { 129 f = 0.58800006F; 130 int i = this.worldObj.getBlockId(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.boundingBox.minY) - 1, MathHelper.floor_double(this.posZ)); 131 132 if (i > 0) 133 { 134 f = Block.blocksList[i].slipperiness * 0.98F; 135 } 136 } 137 138 this.motionX *= (double)f; 139 this.motionY *= 0.9800000190734863D; 140 this.motionZ *= (double)f; 141 142 if (this.onGround) 143 { 144 this.motionY *= -0.5D; 145 } 146 147 ++this.age; 148 149 ItemStack item = getDataWatcher().getWatchableObjectItemStack(10); 150 151 if (!this.worldObj.isRemote && this.age >= lifespan) 152 { 153 if (item != null) 154 { 155 ItemExpireEvent event = new ItemExpireEvent(this, (item.getItem() == null ? 6000 : item.getItem().getEntityLifespan(item, worldObj))); 156 if (MinecraftForge.EVENT_BUS.post(event)) 157 { 158 lifespan += event.extraLife; 159 } 160 else 161 { 162 this.setDead(); 163 } 164 } 165 else 166 { 167 this.setDead(); 168 } 169 } 170 171 if (item != null && item.stackSize <= 0) 172 { 173 this.setDead(); 174 } 175 } 176 177 /** 178 * Looks for other itemstacks nearby and tries to stack them together 179 */ 180 private void searchForOtherItemsNearby() 181 { 182 Iterator iterator = this.worldObj.getEntitiesWithinAABB(EntityItem.class, this.boundingBox.expand(0.5D, 0.0D, 0.5D)).iterator(); 183 184 while (iterator.hasNext()) 185 { 186 EntityItem entityitem = (EntityItem)iterator.next(); 187 this.combineItems(entityitem); 188 } 189 } 190 191 /** 192 * Tries to merge this item with the item passed as the parameter. Returns true if successful. Either this item or 193 * the other item will be removed from the world. 194 */ 195 public boolean combineItems(EntityItem par1EntityItem) 196 { 197 if (par1EntityItem == this) 198 { 199 return false; 200 } 201 else if (par1EntityItem.isEntityAlive() && this.isEntityAlive()) 202 { 203 ItemStack itemstack = this.getEntityItem(); 204 ItemStack itemstack1 = par1EntityItem.getEntityItem(); 205 206 if (itemstack1.getItem() != itemstack.getItem()) 207 { 208 return false; 209 } 210 else if (itemstack1.hasTagCompound() ^ itemstack.hasTagCompound()) 211 { 212 return false; 213 } 214 else if (itemstack1.hasTagCompound() && !itemstack1.getTagCompound().equals(itemstack.getTagCompound())) 215 { 216 return false; 217 } 218 else if (itemstack1.getItem().getHasSubtypes() && itemstack1.getItemDamage() != itemstack.getItemDamage()) 219 { 220 return false; 221 } 222 else if (itemstack1.stackSize < itemstack.stackSize) 223 { 224 return par1EntityItem.combineItems(this); 225 } 226 else if (itemstack1.stackSize + itemstack.stackSize > itemstack1.getMaxStackSize()) 227 { 228 return false; 229 } 230 else 231 { 232 itemstack1.stackSize += itemstack.stackSize; 233 par1EntityItem.delayBeforeCanPickup = Math.max(par1EntityItem.delayBeforeCanPickup, this.delayBeforeCanPickup); 234 par1EntityItem.age = Math.min(par1EntityItem.age, this.age); 235 par1EntityItem.setEntityItemStack(itemstack1); 236 this.setDead(); 237 return true; 238 } 239 } 240 else 241 { 242 return false; 243 } 244 } 245 246 /** 247 * sets the age of the item so that it'll despawn one minute after it has been dropped (instead of five). Used when 248 * items are dropped from players in creative mode 249 */ 250 public void setAgeToCreativeDespawnTime() 251 { 252 this.age = 4800; 253 } 254 255 /** 256 * Returns if this entity is in water and will end up adding the waters velocity to the entity 257 */ 258 public boolean handleWaterMovement() 259 { 260 return this.worldObj.handleMaterialAcceleration(this.boundingBox, Material.water, this); 261 } 262 263 /** 264 * Will deal the specified amount of damage to the entity if the entity isn't immune to fire damage. Args: 265 * amountDamage 266 */ 267 protected void dealFireDamage(int par1) 268 { 269 this.attackEntityFrom(DamageSource.inFire, par1); 270 } 271 272 /** 273 * Called when the entity is attacked. 274 */ 275 public boolean attackEntityFrom(DamageSource par1DamageSource, int par2) 276 { 277 if (this.isEntityInvulnerable()) 278 { 279 return false; 280 } 281 else if (this.getEntityItem() != null && this.getEntityItem().itemID == Item.netherStar.itemID && par1DamageSource.isExplosion()) 282 { 283 return false; 284 } 285 else 286 { 287 this.setBeenAttacked(); 288 this.health -= par2; 289 290 if (this.health <= 0) 291 { 292 this.setDead(); 293 } 294 295 return false; 296 } 297 } 298 299 /** 300 * (abstract) Protected helper method to write subclass entity data to NBT. 301 */ 302 public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound) 303 { 304 par1NBTTagCompound.setShort("Health", (short)((byte)this.health)); 305 par1NBTTagCompound.setShort("Age", (short)this.age); 306 par1NBTTagCompound.setInteger("Lifespan", lifespan); 307 308 if (this.getEntityItem() != null) 309 { 310 par1NBTTagCompound.setCompoundTag("Item", this.getEntityItem().writeToNBT(new NBTTagCompound())); 311 } 312 } 313 314 /** 315 * (abstract) Protected helper method to read subclass entity data from NBT. 316 */ 317 public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound) 318 { 319 this.health = par1NBTTagCompound.getShort("Health") & 255; 320 this.age = par1NBTTagCompound.getShort("Age"); 321 NBTTagCompound nbttagcompound1 = par1NBTTagCompound.getCompoundTag("Item"); 322 this.setEntityItemStack(ItemStack.loadItemStackFromNBT(nbttagcompound1)); 323 324 ItemStack item = getDataWatcher().getWatchableObjectItemStack(10); 325 326 if (item == null || item.stackSize <= 0) 327 { 328 this.setDead(); 329 } 330 331 if (par1NBTTagCompound.hasKey("Lifespan")) 332 { 333 lifespan = par1NBTTagCompound.getInteger("Lifespan"); 334 } 335 } 336 337 /** 338 * Called by a player entity when they collide with an entity 339 */ 340 public void onCollideWithPlayer(EntityPlayer par1EntityPlayer) 341 { 342 if (!this.worldObj.isRemote) 343 { 344 if (this.delayBeforeCanPickup > 0) 345 { 346 return; 347 } 348 349 EntityItemPickupEvent event = new EntityItemPickupEvent(par1EntityPlayer, this); 350 351 if (MinecraftForge.EVENT_BUS.post(event)) 352 { 353 return; 354 } 355 356 ItemStack itemstack = this.getEntityItem(); 357 int i = itemstack.stackSize; 358 359 if (this.delayBeforeCanPickup <= 0 && (event.getResult() == Result.ALLOW || i <= 0 || par1EntityPlayer.inventory.addItemStackToInventory(itemstack))) 360 { 361 if (itemstack.itemID == Block.wood.blockID) 362 { 363 par1EntityPlayer.triggerAchievement(AchievementList.mineWood); 364 } 365 366 if (itemstack.itemID == Item.leather.itemID) 367 { 368 par1EntityPlayer.triggerAchievement(AchievementList.killCow); 369 } 370 371 if (itemstack.itemID == Item.diamond.itemID) 372 { 373 par1EntityPlayer.triggerAchievement(AchievementList.diamonds); 374 } 375 376 if (itemstack.itemID == Item.blazeRod.itemID) 377 { 378 par1EntityPlayer.triggerAchievement(AchievementList.blazeRod); 379 } 380 381 GameRegistry.onPickupNotification(par1EntityPlayer, this); 382 383 this.playSound("random.pop", 0.2F, ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.7F + 1.0F) * 2.0F); 384 par1EntityPlayer.onItemPickup(this, i); 385 386 if (itemstack.stackSize <= 0) 387 { 388 this.setDead(); 389 } 390 } 391 } 392 } 393 394 /** 395 * Gets the username of the entity. 396 */ 397 public String getEntityName() 398 { 399 return StatCollector.translateToLocal("item." + this.getEntityItem().getItemName()); 400 } 401 402 /** 403 * If returns false, the item will not inflict any damage against entities. 404 */ 405 public boolean canAttackWithItem() 406 { 407 return false; 408 } 409 410 /** 411 * Teleports the entity to another dimension. Params: Dimension number to teleport to 412 */ 413 public void travelToDimension(int par1) 414 { 415 super.travelToDimension(par1); 416 417 if (!this.worldObj.isRemote) 418 { 419 this.searchForOtherItemsNearby(); 420 } 421 } 422 423 /** 424 * Returns the ItemStack corresponding to the Entity (Note: if no item exists, will log an error but still return an 425 * ItemStack containing Block.stone) 426 */ 427 public ItemStack getEntityItem() 428 { 429 ItemStack itemstack = this.getDataWatcher().getWatchableObjectItemStack(10); 430 431 if (itemstack == null) 432 { 433 if (this.worldObj != null) 434 { 435 this.worldObj.getWorldLogAgent().func_98232_c("Item entity " + this.entityId + " has no item?!"); 436 } 437 438 return new ItemStack(Block.stone); 439 } 440 else 441 { 442 return itemstack; 443 } 444 } 445 446 /** 447 * Sets the ItemStack for this entity 448 */ 449 public void setEntityItemStack(ItemStack par1ItemStack) 450 { 451 this.getDataWatcher().updateObject(10, par1ItemStack); 452 this.getDataWatcher().setObjectWatched(10); 453 } 454}