Change EntityWizard to implement IMerchant and INpc directly rather than extending EntityVillager, fixes #38 plus a number of other bugs

This commit is contained in:
Electroblob
2018-06-04 16:36:20 +01:00
parent b23a643c0e
commit c129571167
@@ -8,6 +8,8 @@ import java.util.List;
import java.util.Random;
import java.util.Set;
import javax.annotation.Nullable;
import com.google.common.base.Predicate;
import electroblob.wizardry.Wizardry;
@@ -27,25 +29,24 @@ import electroblob.wizardry.util.WizardryParticleType;
import electroblob.wizardry.util.WizardryUtilities;
import io.netty.buffer.ByteBuf;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityAgeable;
import net.minecraft.entity.EntityCreature;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.IEntityLivingData;
import net.minecraft.entity.IMerchant;
import net.minecraft.entity.INpc;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.EntityAIBase;
import net.minecraft.entity.ai.EntityAIHurtByTarget;
import net.minecraft.entity.ai.EntityAILookAtTradePlayer;
import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction;
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
import net.minecraft.entity.ai.EntityAIOpenDoor;
import net.minecraft.entity.ai.EntityAIRestrictOpenDoor;
import net.minecraft.entity.ai.EntityAISwimming;
import net.minecraft.entity.ai.EntityAITradePlayer;
import net.minecraft.entity.ai.EntityAIWander;
import net.minecraft.entity.ai.EntityAIWatchClosest;
import net.minecraft.entity.ai.EntityAIWatchClosest2;
import net.minecraft.entity.effect.EntityLightningBolt;
import net.minecraft.entity.monster.IMob;
import net.minecraft.entity.passive.EntityVillager;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
@@ -75,23 +76,12 @@ import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
import net.minecraftforge.fml.common.registry.VillagerRegistry.VillagerProfession;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import net.minecraftforge.oredict.OreDictionary;
@Mod.EventBusSubscriber
public class EntityWizard extends EntityVillager implements ISpellCaster, IEntityAdditionalSpawnData {
/* After much debugging, the error in the compiled mod (outside of eclipse) was traced back to this class,
* specifically the methods copied in from EntityVillager when I changed this class to extend it. This figures,
* since I had 1.2.1 working just fine before I did that, and it was the only thing I changed. Apparently, methods
* and fields with obfuscated names like func_129090_a can cause problems when compiled. One of the ones here was
* renamed and the other deleted since it was never called. Watch out for this in future (unless, of course, they
* are overriding something, in which case it should be fine). */
// Extending EntityVillager turned out to be a pretty neat thing to do, since now zombies will attack wizards
// TODO: Perhaps we should be implementing IMerchant now instead?
public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISpellCaster, IEntityAdditionalSpawnData {
private EntityAIAttackSpell spellCastingAI = new EntityAIAttackSpell(this, 0.5D, 14.0F, 30, 50);
@@ -100,19 +90,21 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
/** The entity selector passed into the new AI methods. */
protected Predicate<Entity> targetSelector;
/** Copy of EntityVillager's buyingList, renamed to avoid confusion. */
/** The wizard's trades. */
private MerchantRecipeList trades;
/** The wizard's current customer. */
@Nullable
private EntityPlayer customer;
private int timeUntilReset;
/** addDefaultEquipmentAndRecipies is called if this is true */
private boolean updateRecipes;
/** Data parameter for the cooldown time for wizards healing themselves. */
private static final DataParameter<Integer> HEAL_COOLDOWN = EntityDataManager.createKey(EntityWizard.class,
DataSerializers.VARINT);
private static final DataParameter<Integer> HEAL_COOLDOWN = EntityDataManager.createKey(EntityWizard.class, DataSerializers.VARINT);
/** Data parameter for the wizard's element. */
private static final DataParameter<Integer> ELEMENT = EntityDataManager.createKey(EntityWizard.class,
DataSerializers.VARINT);
private static final DataParameter<Integer> ELEMENT = EntityDataManager.createKey(EntityWizard.class, DataSerializers.VARINT);
// Field implementations
private List<Spell> spells = new ArrayList<Spell>(4);
@@ -139,6 +131,8 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
protected void initEntityAI(){
this.tasks.addTask(0, new EntityAISwimming(this));
// Why would you go to the effort of making the IMerchant interface and then have the AI classes only accept
// EntityVillager? N
this.tasks.addTask(1, new EntityAITradePlayer(this));
this.tasks.addTask(1, new EntityAILookAtTradePlayer(this));
this.tasks.addTask(4, new EntityAIRestrictOpenDoor(this));
@@ -222,6 +216,55 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
return this.continuousSpell;
}
@Override
public void setCustomer(EntityPlayer player){
this.customer = player;
}
@Override
public EntityPlayer getCustomer(){
return this.customer;
}
public boolean isTrading(){
return this.getCustomer() != null;
}
@Override
public void verifySellingItem(ItemStack stack){
// Copied from EntityVillager
if(!this.world.isRemote && this.livingSoundTime > -this.getTalkInterval() + 20){
this.livingSoundTime = -this.getTalkInterval();
this.playSound(stack.isEmpty() ? SoundEvents.ENTITY_VILLAGER_NO : SoundEvents.ENTITY_VILLAGER_YES, this.getSoundVolume(), this.getSoundPitch());
}
}
@Override
public World getWorld(){
return this.world;
}
@Override
public BlockPos getPos(){
return new BlockPos(this);
}
@Override
@SideOnly(Side.CLIENT)
public void setRecipes(MerchantRecipeList recipeList){
// Apparently nothing goes here, and nothing's here in EntityVillager either...
}
@Override
public ITextComponent getDisplayName(){
return this.getElement().getWizardName();
}
@Override
protected boolean canDespawn(){
return false;
}
@Override
public void onLivingUpdate(){
@@ -302,8 +345,7 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
}
}
// Super call removed because EntityVillager's version does things I don't want and the next one up is
// in EntityLivingBase and does nothing.
super.updateAITasks(); // This actually does nothing
}
@Override
@@ -339,11 +381,6 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
}
}
@Override
public ITextComponent getDisplayName(){
return this.getElement().getWizardName();
}
@Override
public void writeEntityToNBT(NBTTagCompound nbt){
@@ -382,16 +419,6 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
nbt.getTagList("towerBlocks", NBT.TAG_LONG), (NBTTagLong tag) -> BlockPos.fromLong(tag.getLong())));
}
@Override
protected boolean canDespawn(){
return false;
}
@Override
public boolean isTrading(){
return this.getCustomer() != null;
}
@Override
public void useRecipe(MerchantRecipe merchantrecipe){
@@ -660,11 +687,6 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
return new ItemStack(Blocks.STONE);
}
@Override
public void setProfession(VillagerProfession prof){
// Disables Forge's stuff.
}
@Override
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData livingdata){
@@ -822,49 +844,72 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
}
}
}
// Copied from their respective AI classes
public static class EntityAILookAtTradePlayer extends EntityAIWatchClosest {
private final EntityWizard wizard;
// EntityVillager overrides (that don't add features)
public EntityAILookAtTradePlayer(EntityWizard wizard){
super(wizard, EntityPlayer.class, 8.0F);
this.wizard = wizard;
}
@Override
public boolean isMating(){
return false;
@Override
public boolean shouldExecute(){
if(this.wizard.isTrading()){
this.closestEntity = this.wizard.getCustomer();
return true;
}else{
return false;
}
}
}
public static class EntityAITradePlayer extends EntityAIBase {
private final EntityWizard wizard;
@Override
public void setMating(boolean p_70947_1_){
}
public EntityAITradePlayer(EntityWizard wizard){
this.wizard = wizard;
this.setMutexBits(5);
}
@Override
public void setPlaying(boolean p_70939_1_){
}
@Override
public boolean shouldExecute(){
if(!this.wizard.isEntityAlive()){
return false;
}else if(this.wizard.isInWater()){
return false;
}else if(!this.wizard.onGround){
return false;
}else if(this.wizard.velocityChanged){
return false;
}else{
EntityPlayer entityplayer = this.wizard.getCustomer();
@Override
public boolean isPlaying(){
return false;
}
if(entityplayer == null){
return false;
}else if(this.wizard.getDistanceSq(entityplayer) > 16.0D){
return false;
}else{
return entityplayer.openContainer != null;
}
}
}
@Override
public void setLookingForHome(){
}
@Override
public void startExecuting(){
this.wizard.getNavigator().clearPath();
}
// Doesn't say it, but this is in fact nullable.
@Override
public EntityVillager createChild(EntityAgeable par1EntityAgeable){
return null;
}
@SideOnly(Side.CLIENT)
@Override
public void setRecipes(MerchantRecipeList par1MerchantRecipeList){
}
@Override
public void onStruckByLightning(EntityLightningBolt lightningBolt){
// Restores the normal behaviour, replacing EntityVillager's witch conversion.
this.attackEntityFrom(DamageSource.LIGHTNING_BOLT, 5.0F);
// Entity's version does something strange with the private fire variable, but since I don't have access this
// will probably be fine.
this.setFire(8);
@Override
public void resetTask(){
this.wizard.setCustomer((EntityPlayer)null);
}
}
}