package joshie.harvest.animals.stats; import joshie.harvest.animals.HFAnimals; import joshie.harvest.animals.item.ItemAnimalTreat.Treat; import joshie.harvest.animals.packet.PacketSyncAnimal; import joshie.harvest.animals.packet.PacketSyncHappiness; import joshie.harvest.api.HFApi; import joshie.harvest.api.animals.AnimalAction; import joshie.harvest.api.animals.AnimalStats; import joshie.harvest.api.animals.AnimalTest; import joshie.harvest.api.animals.IAnimalType; import joshie.harvest.api.calendar.Season; import joshie.harvest.api.player.RelationshipType; import joshie.harvest.core.helpers.EntityHelper; import joshie.harvest.core.network.PacketHandler; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.passive.EntityAnimal; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.MobEffects; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.potion.PotionEffect; import net.minecraft.util.DamageSource; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.ReflectionHelper; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.lang.ref.WeakReference; import java.lang.reflect.InvocationTargetException; import java.util.Random; import java.util.UUID; import static joshie.harvest.core.network.PacketHandler.sendToEveryone; public class AnimalStatsHF implements AnimalStats<NBTTagCompound> { protected static final Random rand = new Random(); private WeakReference<EntityPlayer> owner; protected EntityAnimal animal; protected IAnimalType type; //TODO: Remove in 0.7+ protected UUID o_uuid; private int currentLifespan = 0; //How many days this animal has lived for private int daysNotFed; //How many subsequent days that this animal has not been fed private boolean loadHappiness = false; //TODO: Remove in 0.7+ private boolean beenLoved; //If the animal has received love today private int happiness; //How happy this animal is private boolean isSick; //Whether the animal is sick or not private boolean wasSick; //Whether the animal was previously sick private boolean hasDied; //Whether this animal is classed as dead //Product based stuff private int daysPassed; //How many days have passed so far private int productsPerDay = 1; //The maximum number of products this animal can produce a day private int producedProducts; //Whether the animal has produced products this day private boolean golden; //If this animal has won a contest private boolean treated; //Whether this animal has had it's treat for today private int genericTreats; //Number of generic treats this animal had private int typeTreats; //Number of specific treats this animal had private boolean wasOutsideInSun; public AnimalStatsHF() { this.type = HFAnimals.CHICKENS; } /** Set the animal type * @param type the animal type **/ public AnimalStatsHF setType(IAnimalType type) { this.type = type; return this; } /** Set the animal entity * @param animal the entity **/ public AnimalStatsHF setEntity(EntityAnimal animal) { this.animal = animal; //TODO: Remove in 0.7+ //Loads the happiness based on the entity if (loadHappiness) { EntityPlayer owner = getOwner(); if (owner != null) { loadHappiness = false; //Reset the happiness loading happiness = RelationshipType.ANIMAL.getMaximumRP() / 2; } } return this; } @Override public IAnimalType getType() { return type; } @Override public EntityAnimal getAnimal() { return animal; } private int getDeathChance() { //If the animal has not been fed, give it a fix changed of dying if (daysNotFed > 0) { return Math.max(1, 45 - daysNotFed * 3); } //Gets the adjusted relationship, 0-35k double chance = (happiness / (double) RelationshipType.ANIMAL.getMaximumRP()) * 200; if (chance <= 1) { chance = 1D; } return (int) chance; } public void setDead() { this.hasDied = true; } @Override public int getProductsPerDay() { return productsPerDay; } @Override public void onBihourlyTick() { World world = animal.worldObj; boolean dayTime = world.isDaytime(); boolean isRaining = world.isRaining(); boolean isOutside = world.canBlockSeeSky(new BlockPos(animal)); boolean isOutsideInSun = !isRaining && isOutside && dayTime && HFApi.calendar.getDate(world).getSeason() != Season.WINTER; if (isOutsideInSun && wasOutsideInSun) { affectHappiness(type.getRelationshipBonus(AnimalAction.OUTSIDE)); } //Mark the past value wasOutsideInSun = isOutsideInSun; } protected void preStress() {} protected void postStress() {} protected void updateStats() { //Update the maximum produced products if (treated && productsPerDay < 5) { int requiredGeneric = type.getGenericTreatCount(); int requiredType = type.getTypeTreatCount(); if (genericTreats >= requiredGeneric && requiredType >= typeTreats) { genericTreats -= requiredGeneric; typeTreats -= requiredType; productsPerDay++; } } treated = false; } protected void updatePregnancy() {} @Override public boolean newDay() { if (animal != null) { //Check if the animal is going to die if (hasDied) return false; if (currentLifespan > type.getMaxLifespan()) return false; if (currentLifespan > type.getMinLifespan()) { if (rand.nextInt(getDeathChance()) == 0) { hasDied = true; return false; } } //If the animal is not sick, check the healthiness if (!isSick && daysNotFed >= 0) { isSick = true; //Make the animal sick } else if (isSick && daysNotFed < 0) isSick = false; //Reset everything and increase where appropriate currentLifespan++; preStress(); postStress(); beenLoved = false; daysNotFed++; daysPassed++; updateStats(); //Updating potion effects on the animal if (isSick) { wasSick = true; animal.addPotionEffect(new PotionEffect(MobEffects.NAUSEA, 1000000, 0)); animal.addPotionEffect(new PotionEffect(MobEffects.BLINDNESS, 1000000, 0)); animal.addPotionEffect(new PotionEffect(MobEffects.SLOWNESS, 1000000, 0)); } else if (wasSick) { wasSick = false; animal.removePotionEffect(MobEffects.NAUSEA); animal.removePotionEffect(MobEffects.BLINDNESS); animal.removePotionEffect(MobEffects.SLOWNESS); } //Updating grabbing products between animals int daysBetween = type.getDaysBetweenProduction(); if (daysBetween > 0) { if (daysPassed >= daysBetween) { daysPassed = 0; producedProducts = 0; type.refreshProduct(this, animal); } } updatePregnancy(); sendToEveryone(new PacketSyncAnimal(animal.getEntityId(), this)); return true; } else return false; } //TODO: Remove in 0.7+ private EntityPlayer getAndCreateOwner() { if (owner != null) return owner.get(); else { if (o_uuid != null) { owner = new WeakReference<>(EntityHelper.getPlayerFromUUID(o_uuid)); return owner.get(); } } return null; } //TODO: Remove in 0.7+ public EntityPlayer getOwner() { EntityPlayer owner = getAndCreateOwner(); if (owner != null && animal != null) { if (animal.worldObj.provider.getDimension() == owner.worldObj.provider.getDimension()) { if (animal.getDistanceToEntity(owner) <= 178D) { return owner; } } } return null; } @Override public boolean canProduce() { return !isSick && producedProducts < productsPerDay; } @Override public void setProduced(int amount) { producedProducts += amount; affectHappiness(getType().getRelationshipBonus(AnimalAction.CLAIM_PRODUCT)); HFApi.animals.syncAnimalStats(animal); } @Override public boolean performTest(AnimalTest test) { if (test == AnimalTest.HAS_EATEN) return daysNotFed < 0; else if (test == AnimalTest.IS_SICK) return isSick; else if (test == AnimalTest.HAD_TREAT) return treated; else if (test == AnimalTest.BEEN_LOVED) return beenLoved; else return test == AnimalTest.WON_CONTEST && golden; } @Override public boolean performAction(@Nonnull World world, @Nullable ItemStack stack, AnimalAction action) { if (action == AnimalAction.FEED) return feed(world); else if (action == AnimalAction.HEAL) return heal(world); else if (action == AnimalAction.MAKE_GOLDEN) return golden(world); else if (action == AnimalAction.PETTED) return pet(world); return (action == AnimalAction.TREAT_SPECIAL || action == AnimalAction.TREAT_GENERIC) && treat(world, stack); } private boolean pet(@Nonnull World world) { if (!beenLoved) { if (!world.isRemote) { beenLoved = true; affectHappiness(type.getRelationshipBonus(AnimalAction.PETTED)); HFApi.animals.syncAnimalStats(animal); } return true; } return false; } private boolean golden(@Nonnull World world) { if (!golden) { if (!world.isRemote) { golden = true; HFApi.animals.syncAnimalStats(animal); } return true; } return false; } private boolean feed(@Nonnull World world) { if (daysNotFed >= 0) { if (!world.isRemote) { daysNotFed = -1; affectHappiness(type.getRelationshipBonus(AnimalAction.FEED)); HFApi.animals.syncAnimalStats(animal); } return true; } return false; } private boolean heal(@Nonnull World world) { if (isSick) { animal.clearActivePotions(); if (!world.isRemote) { isSick = false; affectHappiness(type.getRelationshipBonus(AnimalAction.HEAL)); HFApi.animals.syncAnimalStats(animal); } return true; } else return false; } @Override public int getHappiness() { return happiness; } @Override public void affectHappiness(int amount) { if (amount != 0) { happiness = Math.max(0, Math.min(RelationshipType.ANIMAL.getMaximumRP(), happiness + amount)); if (animal != null && !animal.worldObj.isRemote) { if (amount < 0) { try { ReflectionHelper.findMethod(EntityLivingBase.class, null, new String[]{ "playHurtSound", "func_184581_c" }, DamageSource.class).invoke(animal, DamageSource.starve); } catch (IllegalAccessException | InvocationTargetException ex) { ex.printStackTrace(); } } PacketHandler.sendToAllAround(new PacketSyncHappiness(animal.getEntityId(), amount), animal.dimension, animal.posX, animal.posY, animal.posZ, 178); } } } @Override public void copyHappiness(@Nullable EntityPlayer player, int parentHappiness, double percentage) { happiness = (int)(parentHappiness * (percentage / 100D)); if (getAnimal() != null) { HFApi.animals.syncAnimalStats(getAnimal()); } } private void treat(AnimalAction action) { treated = true; affectHappiness(type.getRelationshipBonus(action)); HFApi.animals.syncAnimalStats(animal); } private boolean treat(@Nonnull World world, @Nullable ItemStack stack) { if (stack == null) return false; if (!treated) { if (HFAnimals.TREATS.getEnumFromStack(stack) == Treat.GENERIC) { if (!world.isRemote) { genericTreats++; treat(AnimalAction.TREAT_GENERIC); } return true; } else if (HFAnimals.TREATS.getEnumFromStack(stack).getType() == type) { if (!world.isRemote) { typeTreats++; treat(AnimalAction.TREAT_SPECIAL); } return true; } else { if (!world.isRemote) { treat(AnimalAction.TREAT_INCORRECT); } return true; } } return false; } @Override public void deserializeNBT(NBTTagCompound nbt) { //TODO: Remove in 0.7+ if (nbt.hasKey("Owner")) { o_uuid = UUID.fromString(nbt.getString("Owner")); } currentLifespan = nbt.getShort("CurrentLifespan"); daysNotFed = nbt.getByte("DaysNotFed"); daysPassed = nbt.getByte("DaysPassed"); treated = nbt.getBoolean("Treated"); genericTreats = nbt.getShort("GenericTreats"); typeTreats = nbt.getShort("TypeTreats"); wasSick = nbt.getBoolean("WasSick"); isSick = nbt.getBoolean("IsSick"); hasDied = nbt.getBoolean("IsDead"); wasOutsideInSun = nbt.getBoolean("WasOutsideInSun"); golden = nbt.getBoolean("Golden"); if (type.getDaysBetweenProduction() > 0) { productsPerDay = nbt.getByte("NumProducts"); producedProducts = nbt.getByte("ProducedProducts"); } if (nbt.hasKey("Happiness")) { beenLoved = nbt.getBoolean("BeenLoved"); happiness = nbt.getShort("Happiness"); } else loadHappiness = true; } @Override public NBTTagCompound serializeNBT() { NBTTagCompound tag = new NBTTagCompound(); //TODO: Remove in 0.7+ if (o_uuid != null) { tag.setString("Owner", o_uuid.toString()); } tag.setShort("CurrentLifespan", (short) currentLifespan); tag.setByte("DaysNotFed", (byte) daysNotFed); tag.setByte("DaysPassed", (byte) daysPassed); tag.setBoolean("Treated", treated); tag.setShort("GenericTreats", (short) genericTreats); tag.setShort("TypeTreats", (short) typeTreats); tag.setBoolean("WasSick", wasSick); tag.setBoolean("IsSick", isSick); tag.setBoolean("IsDead", hasDied); tag.setBoolean("WasOutsideInSun", wasOutsideInSun); tag.setBoolean("Golden", golden); if (type.getDaysBetweenProduction() > 0) { tag.setByte("NumProducts", (byte) productsPerDay); tag.setByte("ProducedProducts", (byte) producedProducts); } tag.setBoolean("BeenLoved", beenLoved); tag.setShort("Happiness", (short) happiness); return tag; } }