/**
Copyright (C) <2017> <coolAlias>
This file is part of coolAlias' Zelda Sword Skills Minecraft Mod; as such,
you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package zeldaswordskills.entity.npc;
import com.google.common.base.Predicate;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityCreature;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.IEntityLivingData;
import net.minecraft.entity.INpc;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.EntityAIAvoidEntity;
import net.minecraft.entity.ai.EntityAIMoveIndoors;
import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction;
import net.minecraft.entity.ai.EntityAIOpenDoor;
import net.minecraft.entity.ai.EntityAIRestrictOpenDoor;
import net.minecraft.entity.ai.EntityAISwimming;
import net.minecraft.entity.ai.EntityAIWander;
import net.minecraft.entity.ai.EntityAIWatchClosest;
import net.minecraft.entity.ai.EntityAIWatchClosest2;
import net.minecraft.entity.monster.IMob;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.pathfinding.PathNavigateGround;
import net.minecraft.util.BlockPos;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.village.Village;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import zeldaswordskills.ref.Config;
/**
*
* Generic NPC class adds default villager-like AI and keeps close to a village when possible.
*
* NPCs will not despawn and, depending on config settings, are invulnerable.
*
*/
public abstract class EntityNpcBase extends EntityCreature implements INpc
{
/** Village search timer */
private int randomTickDivider;
/** Nearest village object, for path-finding */
public Village villageObj;
public EntityNpcBase(World world) {
super(world);
this.setSize(0.6F, 1.8F);
this.enablePersistence();
((PathNavigateGround) this.getNavigator()).setBreakDoors(true);
((PathNavigateGround) this.getNavigator()).setAvoidsWater(true);
this.tasks.addTask(0, new EntityAISwimming(this));
this.tasks.addTask(1, new EntityAIAvoidEntity<Entity>(this, Entity.class, new Predicate<Entity>() {
public boolean apply(Entity entity) {
return entity instanceof IMob;
}
}, 8.0F, 0.6D, 0.6D));
this.tasks.addTask(2, new EntityAIMoveIndoors(this));
this.tasks.addTask(3, new EntityAIRestrictOpenDoor(this));
this.tasks.addTask(4, new EntityAIOpenDoor(this, true));
this.tasks.addTask(5, new EntityAIMoveTowardsRestriction(this, 0.6D));
this.tasks.addTask(6, new EntityAIWatchClosest2(this, EntityPlayer.class, 3.0F, 1.0F));
this.tasks.addTask(6, new EntityAIWander(this, 0.6D));
this.tasks.addTask(7, new EntityAIWatchClosest(this, EntityLiving.class, 8.0F));
}
/**
* Returning a string here will automatically name the entity when spawned.
* Null or an empty string are both allowed, and will simply not apply a name.
*/
protected abstract String getNameTagOnSpawn();
/**
* Sets the entity's custom name tag when spawned, if applicable
*/
private void setNameOnSpawn() {
String name = getNameTagOnSpawn();
if (name != null && name.length() > 0) {
setCustomNameTag(name);
}
}
@Override
protected void applyEntityAttributes() {
super.applyEntityAttributes();
this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.5D);
}
@Override
public boolean allowLeashing() {
return false;
}
@Override
protected boolean canDespawn() {
return false;
}
@Override
public boolean isEntityInvulnerable(DamageSource source) {
return Config.areNpcsInvulnerable();
}
@Override
protected void updateAITick() {
if (--randomTickDivider <= 0) {
BlockPos pos = new BlockPos(this);
worldObj.getVillageCollection().addToVillagerPositionList(pos);
randomTickDivider = 70 + rand.nextInt(50);
villageObj = worldObj.getVillageCollection().getNearestVillage(pos, 32);
if (villageObj == null) {
detachHome();
} else {
pos = villageObj.getCenter();
setHomePosAndDistance(pos, (int)((float) villageObj.getVillageRadius() * 1.0F));
}
randomUpdateTick();
}
super.updateAITick();
}
/**
* Update that occurs every 70-120 ticks, after the NPC has searched for a village object
*/
protected void randomUpdateTick() {}
@Override
@SideOnly(Side.CLIENT)
public void handleStatusUpdate(byte flag) {
switch(flag) {
case 12:
generateRandomParticles(EnumParticleTypes.HEART);
break;
case 13:
generateRandomParticles(EnumParticleTypes.VILLAGER_ANGRY);
break;
case 14:
generateRandomParticles(EnumParticleTypes.VILLAGER_HAPPY);
break;
default:
super.handleStatusUpdate(flag);
}
}
@SideOnly(Side.CLIENT)
private void generateRandomParticles(EnumParticleTypes particle) {
for (int i = 0; i < 5; ++i) {
double d0 = rand.nextGaussian() * 0.02D;
double d1 = rand.nextGaussian() * 0.02D;
double d2 = rand.nextGaussian() * 0.02D;
worldObj.spawnParticle(particle,
posX + (double)(rand.nextFloat() * width * 2.0F) - (double) width,
posY + 1.0D + (double)(rand.nextFloat() * height),
posZ + (double)(rand.nextFloat() * width * 2.0F) - (double) width, d0, d1, d2);
}
}
@Override
public void setRevengeTarget(EntityLivingBase entity) {
super.setRevengeTarget(entity);
if (villageObj != null && entity != null) {
villageObj.addOrRenewAgressor(entity);
if (entity instanceof EntityPlayer) {
int rep = (isChild() ? -3 : -1);
villageObj.setReputationForPlayer(entity.getName(), rep);
if (isEntityAlive()) {
worldObj.setEntityState(this, (byte) 13);
}
}
}
}
@Override
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData data) {
data = super.onInitialSpawn(difficulty, data);
setNameOnSpawn();
return data;
}
}