User:Libxfs/pigzombie

The following is static analysis of Minecraft Alpha 1.2.6 source code decompiled and de-obfuscated with Minecraft Coder Pack. You can ask me for the source code for research purposes because it's intelletual property of Notch which I don't have the right to publish or distribute.

Naming
Notch calls it Pig Zombie or Zombie Pig in his official releases. Notch codes it with internal names as pigzombie or zombiepig.
 * pigman site:notch.tumblr.com
 * pigmen site:notch.tumblr.com
 * zombie pigs site:notch.tumblr.com

Code excerpt from EntitiyPigZombie.java

public class EntityPigZombie extends EntityZombie { ...

public EntityPigZombie(World world) { ...	texture = "/mob/pigzombie.png"; ... }

public void onUpdate { ...		worldObj.playSoundAtEntity(this, "mob.zombiepig.zpigangry", ...); }

protected String getLivingSound {	return "mob.zombiepig.zpig"; }

protected String getHurtSound {	return "mob.zombiepig.zpighurt"; }

protected String getDeathSound {	return "mob.zombiepig.zpigdeath"; }

The conlusion is that the current "Zombie Pigman" is not official naming.

Why they never forgive
There is only one property of EntityPigZombie defining its hostility: private int angerLevel; which is only referenced in: ~/src/mcp25/alphasrc$ grep angerLevel -r * minecraft/net/minecraft/src/EntityPigZombie.java:       angerLevel = 0; minecraft/net/minecraft/src/EntityPigZombie.java:       nbttagcompound.setShort("Anger", (short)angerLevel); minecraft/net/minecraft/src/EntityPigZombie.java:       angerLevel = nbttagcompound.getShort("Anger"); minecraft/net/minecraft/src/EntityPigZombie.java:       if(angerLevel == 0) minecraft/net/minecraft/src/EntityPigZombie.java:       angerLevel = 400 + rand.nextInt(400); minecraft/net/minecraft/src/EntityPigZombie.java:   private int angerLevel;

The zero assignment happens in the constructor: public EntityPigZombie(World world) {	super(world); angerLevel = 0; ... }

As you can see, it does not decrease. It would a logical fallacy of argument from authority if you assert that they forgive now by simply citing Notch's statement as fact without providing sufficient proof.

Anger behavior
Another assignment happens here: private void becomeAngryAt(Entity entity) {	playerToAttack = entity; angerLevel = 400 + rand.nextInt(400); randomSoundDelay = rand.nextInt(40); } which is only referenced by this function which determines how pigmen can be attacked by other entities: public boolean canAttackEntity(Entity attackingEntity, int damage) {	if (attackingEntity instanceof EntityPlayer) { List list = worldObj.getEntitiesWithinAABBExcludingEntity(this, boundingBox.expands(32, 32, 32)); for(int j = 0; j < list.size; j++) { Entity nearbyEntity = (Entity)list.get(j); if (nearbyEntity instanceof EntityPigZombie) { EntityPigZombie nearbyPigZombie = (EntityPigZombie)nearbyEntity; nearbyPigZombie.becomeAngryAt(entity); }		}

becomeAngryAt(attackingEntity); }       return super.canAttackEntity(attackingEntity, damage); } This means they only become angry when attacked by players.

And their anger level affects their behavior of target finding: protected Entity findPlayerToAttack {	if (angerLevel == 0) { return null; } else { return super.findPlayerToAttack; } }

How do they choose attack target
The direct superclass of EntityPigZombie is EntityZombie which doesn't overload findPlayerToAttack or canAttackEntity.

The direct superclass of EntityZombie is EntityMobs which overloads findPlayerToAttack or canAttackEntity as:

EntityMobs.java: protected Entity findPlayerToAttack {	EntityPlayer player = worldObj.getClosestPlayerToEntity(this, 16); if (player != null && canEntityBeSeen(player)) { return player; } else { return null; } }

public boolean canAttackEntity(Entity entity, int damage) {	if (super.canAttackEntity(entity, damage)) { if(riddenByEntity == entity || ridingEntity == entity) { return true; }		if(entity != this) { playerToAttack = entity; }		return true; } else { return false; } }

EntityMobs.canAttackEntity defines that when a Mob is attacked, it will set its target (playerToAttack) to who attacks it.

The direct superclass of EntityMobs is EntityCreature which defines findPlayerToAttack as a trivial function that returns null.

The playerToAttack is referenced as follows: ~/src/mcp25/alphasrc$ grep playerToAttack * -r minecraft/net/minecraft/src/EntitySpider.java:           playerToAttack = null; minecraft/net/minecraft/src/EntityMobs.java:               playerToAttack = entity; minecraft/net/minecraft/src/EntityPigZombie.java:       field_9333_am = playerToAttack == null ? 0.5F : 0.95F; minecraft/net/minecraft/src/EntityPigZombie.java:       playerToAttack = entity; minecraft/net/minecraft/src/EntityCreature.java:       if(playerToAttack == null) minecraft/net/minecraft/src/EntityCreature.java:           playerToAttack = findPlayerToAttack; minecraft/net/minecraft/src/EntityCreature.java:           if(playerToAttack != null) minecraft/net/minecraft/src/EntityCreature.java:               pathToEntity = worldObj.getPathToEntity(this, playerToAttack, f); minecraft/net/minecraft/src/EntityCreature.java:       if(!playerToAttack.isEntityAlive) minecraft/net/minecraft/src/EntityCreature.java:           playerToAttack = null; minecraft/net/minecraft/src/EntityCreature.java:           float f1 = playerToAttack.getDistanceToEntity(this); minecraft/net/minecraft/src/EntityCreature.java:           if(canEntityBeSeen(playerToAttack)) minecraft/net/minecraft/src/EntityCreature.java:               attackEntity(playerToAttack, f1); minecraft/net/minecraft/src/EntityCreature.java:       if(!hasAttacked && playerToAttack != null && (pathToEntity == null || rand.nextInt(20) == 0)) minecraft/net/minecraft/src/EntityCreature.java:           pathToEntity = worldObj.getPathToEntity(this, playerToAttack, f); minecraft/net/minecraft/src/EntityCreature.java:           if(hasAttacked && playerToAttack != null) minecraft/net/minecraft/src/EntityCreature.java:               double d4 = playerToAttack.posX - posX; minecraft/net/minecraft/src/EntityCreature.java:               double d5 = playerToAttack.posZ - posZ; minecraft/net/minecraft/src/EntityCreature.java:       if(playerToAttack != null) minecraft/net/minecraft/src/EntityCreature.java:           faceEntity(playerToAttack, 30F); minecraft/net/minecraft/src/EntityCreature.java:   protected Entity playerToAttack;

Only these are assignments to playerToAttack: minecraft/net/minecraft/src/EntitySpider.java:           playerToAttack = null; minecraft/net/minecraft/src/EntityMobs.java:               playerToAttack = entity; minecraft/net/minecraft/src/EntityPigZombie.java:       playerToAttack = entity; minecraft/net/minecraft/src/EntityCreature.java:           playerToAttack = findPlayerToAttack; minecraft/net/minecraft/src/EntityCreature.java:           playerToAttack = null; where EntitySpider is unrelated to this analysis, and the assignment in EntityMobs and EntityPigZombie has been listed above.

The only two left assignments: EntityCreature.java: ...	if (playerToAttack == null) { playerToAttack = findPlayerToAttack; if (playerToAttack != null) { pathToEntity = worldObj.getPathToEntity(this, playerToAttack, f); }	} else if(!playerToAttack.isEntityAlive) { playerToAttack = null; } else ...

So when the playerToAttack is already set to non-null entity, the findPlayerToAttack is actually not called.

And this is possible to happen in EntityMobs.canAttackEntity.

A thought experiment of how Pigmen turn hostile to Ghasts
When a fireball launched by a Ghast hits a Pigman: EntityFireball.java: if (movingobjectposition != null) { if (movingobjectposition.entityHit != null) { if (!movingobjectposition.entityHit.canAttackEntity(launchedBy, 0));//launchedBy: field_9397_j }		worldObj.createExplosion(null, posX, posY, posZ, 1.0F, true);//createExplosion: func_12244_a setEntityDead; }

Then EntityPigZombie.canAttackEntity is called, as the attacking entity is not EntityPlayer, so EntityMobs.canAttackEntity is called.

In EntityMobs.canAttackEntity, if this attack is successful, playerToAttack will be set to the Ghast, and will not be subsequently affected by findPlayerToAttack.

This is how Pigmen turn hostile to Ghasts.

Fire Immunity
Fire damage only takes place in three locations: Entity.java: ...       if (worldObj.isBoundingBoxBurning(boundingBox)) { func_355_a(1); if(!flag2) { fire++; if (fire == 0) { fire = 300; }		}       } else ...	if (fire > 0) { if (isImmuneToFire) { fire -= 4; if (fire < 0) fire = 0; } else { if(fire % 20 == 0) canAttackEntity(null, 1); fire--; }	}	if (handleLavaMovement) { func_4038_J; }

where func_355_a and func_4038_J are: Entity.java: protected void func_355_a(int i) { if (!isImmuneToFire) { canAttackEntity(null, i); } } protected void func_4038_J {	if (!isImmuneToFire) { canAttackEntity(null, 4); fire = 600; } }

All of fire damage is controlled by isImmuneToFire flag which is true for EntityPigZombie.

Feather drops
On living entities death: EntityLiving.java: public void onDeath(Entity entity) { ...	int i = getDropItemId; if (i > 0) { int j = rand.nextInt(3); for(int k = 0; k < j; k++) { dropItem(i, 1); }	} ... drop 0-2 items of getDropItemId which is: EntityPigZombie.java: protected int getDropItemId {	return Item.porkCooked.shiftedIndex; }