diff --git a/src/main/java/net/seanomik/tamablefoxes/EntityTamableFox.java b/src/main/java/net/seanomik/tamablefoxes/EntityTamableFox.java index e5eab54..c8a5a47 100644 --- a/src/main/java/net/seanomik/tamablefoxes/EntityTamableFox.java +++ b/src/main/java/net/seanomik/tamablefoxes/EntityTamableFox.java @@ -1,11 +1,12 @@ package net.seanomik.tamablefoxes; -import com.mojang.datafixers.Dynamic; import net.minecraft.server.v1_15_R1.*; import net.seanomik.tamablefoxes.io.Config; +import net.seanomik.tamablefoxes.io.LanguageConfig; import net.seanomik.tamablefoxes.versions.version_1_15.pathfinding.*; -import org.bukkit.ChatColor; import org.bukkit.NamespacedKey; +import org.bukkit.OfflinePlayer; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity; import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; import org.bukkit.craftbukkit.v1_15_R1.persistence.CraftPersistentDataContainer; import org.bukkit.entity.Item; @@ -13,29 +14,37 @@ import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; import org.bukkit.scheduler.BukkitRunnable; -import javax.annotation.Nullable; +import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; +import java.util.*; public class EntityTamableFox extends EntityFox { + List untamedGoals = new ArrayList<>(); private boolean tamed; private boolean sitting; - private boolean sleeping; - private String chosenName; private EntityLiving owner; private UUID ownerUUID; - private FoxPathfinderGoalSit goalSit; - private PathfinderGoal goalRandomSitting; - private PathfinderGoal goalBerryPicking; - private PathfinderGoal goalFleeSun; - private PathfinderGoal goalNearestVillage; public EntityTamableFox(EntityTypes entitytypes, World world) { super(entitytypes, world); + + clearPathFinderGoals(); + initPathfinderGoals(); + } + + public static Object getPrivateField(String fieldName, Class clazz, Object object) { + Field field; + Object o = null; + try { + field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + o = field.get(object); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + return o; } private PathfinderGoal getFoxInnerPathfinderGoal(String innerName, List args, List> argTypes) { @@ -47,18 +56,37 @@ public class EntityTamableFox extends EntityFox { } @Override - protected void initPathfinder() { + protected void initAttributes() { + this.getAttributeMap().b(GenericAttributes.MAX_HEALTH); + this.getAttributeMap().b(GenericAttributes.KNOCKBACK_RESISTANCE); + this.getAttributeMap().b(GenericAttributes.MOVEMENT_SPEED); + this.getAttributeMap().b(GenericAttributes.ARMOR); + this.getAttributeMap().b(GenericAttributes.ARMOR_TOUGHNESS); + + // Default value is 32, might want to make this configurable in the future + this.getAttributeMap().b(GenericAttributes.FOLLOW_RANGE).setValue(16.0D); + + this.getAttributeMap().b(GenericAttributes.ATTACK_KNOCKBACK); + + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.30000001192092896D); + + // Default value is 10, might want to make this configurable in the future + this.getAttributeInstance(GenericAttributes.MAX_HEALTH).setValue(24.0D); + + // Default value is 2, might want to make this configurable in the future + this.getAttributeMap().b(GenericAttributes.ATTACK_DAMAGE).setValue(3.0D); + } + + private void initPathfinderGoals() { try { + this.goalSelector.a(0, getFoxInnerPathfinderGoal("g")); // FloatGoal + this.goalSit = new FoxPathfinderGoalSit(this); - this.goalSelector.a(0, getFoxInnerPathfinderGoal("g")); // Swim - this.goalSelector.a(1, this.goalSit); - this.goalSelector.a(1, getFoxInnerPathfinderGoal("b")); // Unknown + this.goalSelector.a(1, goalSit); - // Panic - this.goalSelector.a(2, new FoxPathfinderGoalPanic(this, 2.2D)); - - // Breed - this.goalSelector.a(3, getFoxInnerPathfinderGoal("n", Arrays.asList(1.0D), Arrays.asList(double.class))); + this.goalSelector.a(1, getFoxInnerPathfinderGoal("b")); // FaceplantGoal + this.goalSelector.a(2, new FoxPathfinderGoalPanic(this, 2.2D)); // PanicGoal + this.goalSelector.a(3, getFoxInnerPathfinderGoal("e", Arrays.asList(1.0D), Arrays.asList(double.class))); // BreedGoal // Avoid human only if not tamed this.goalSelector.a(4, new PathfinderGoalAvoidTarget(this, EntityHuman.class, 16.0F, 1.6D, 1.4D, (entityliving) -> !tamed)); @@ -76,35 +104,38 @@ public class EntityTamableFox extends EntityFox { return !((EntityWolf) entityliving).isTamed(); } })); + this.goalSelector.a(4, new FoxPathfinderGoalMeleeAttack(this, 1.2000000476837158D, true)); this.goalSelector.a(5, new FoxPathfinderGoalFollowOwner(this, 1.3D, 10.0F, 2.0F, false)); - this.goalSelector.a(6, getFoxInnerPathfinderGoal("u")); // Lunge shake - this.goalSelector.a(7, new EntityFox.o()); // Lunge + this.goalSelector.a(6, getFoxInnerPathfinderGoal("u")); // StalkPrey + this.goalSelector.a(7, new EntityFox.o()); // Pounce - // Flee sun - goalFleeSun = getFoxInnerPathfinderGoal("s", Arrays.asList(1.25D), Arrays.asList(double.class)); - this.goalSelector.a(7, goalFleeSun); + PathfinderGoal seekShelter = getFoxInnerPathfinderGoal("s", Arrays.asList(1.25D), Arrays.asList(double.class)); + this.goalSelector.a(7, seekShelter); // SeekShelter + untamedGoals.add(seekShelter); - this.goalSelector.a(8, getFoxInnerPathfinderGoal("t")); // Sleeping under trees - this.goalSelector.a(9, getFoxInnerPathfinderGoal("h", Arrays.asList(this, 1.25D), Arrays.asList(EntityFox.class, double.class))); // Follow parent + this.goalSelector.a(8, getFoxInnerPathfinderGoal("t")); // Sleep + this.goalSelector.a(9, getFoxInnerPathfinderGoal("h", Arrays.asList(this, 1.25D), Arrays.asList(EntityFox.class, double.class))); // FollowParent - // Nearest village - goalNearestVillage = getFoxInnerPathfinderGoal("q", Arrays.asList(32, 200), Arrays.asList(int.class, int.class)); - this.goalSelector.a(9, goalNearestVillage); + PathfinderGoal strollThroughVillage = getFoxInnerPathfinderGoal("q", Arrays.asList(32, 200), Arrays.asList(int.class, int.class)); + this.goalSelector.a(9, strollThroughVillage); // StrollThroughVillage + untamedGoals.add(strollThroughVillage); - // Pick berry bushes - goalBerryPicking = new EntityFox.f(1.2000000476837158D, 12, 2); - this.goalSelector.a(10, goalBerryPicking); + // EatBerries (Pick berry bushes) + PathfinderGoal eatBerries = new EntityFox.f(1.2000000476837158D, 12, 2); + this.goalSelector.a(10, eatBerries); + untamedGoals.add(eatBerries); // Maybe this should be configurable too? this.goalSelector.a(10, new PathfinderGoalLeapAtTarget(this, 0.4F)); this.goalSelector.a(11, new PathfinderGoalRandomStrollLand(this, 1.15D)); - this.goalSelector.a(11, getFoxInnerPathfinderGoal("p")); // If a item is on the ground, go to it and take it - this.goalSelector.a(12, getFoxInnerPathfinderGoal("j", Arrays.asList(this, EntityHuman.class, 24.0f), Arrays.asList(EntityInsentient.class, Class.class, float.class))); // Look at player + this.goalSelector.a(11, getFoxInnerPathfinderGoal("p")); // SearchForItems + this.goalSelector.a(12, getFoxInnerPathfinderGoal("j", Arrays.asList(this, EntityHuman.class, 24.0f), Arrays.asList(EntityInsentient.class, Class.class, float.class))); // LookAtPlayer - // The random sitting(?) - this.goalRandomSitting = getFoxInnerPathfinderGoal("r"); - this.goalSelector.a(13, goalRandomSitting); + // PerchAndSearch (Random sitting?) + PathfinderGoal perchAndSearch = getFoxInnerPathfinderGoal("r"); + this.goalSelector.a(13, perchAndSearch); + untamedGoals.add(perchAndSearch); this.targetSelector.a(1, new FoxPathfinderGoalOwnerHurtByTarget(this)); this.targetSelector.a(2, new FoxPathfinderGoalOwnerHurtTarget(this)); @@ -112,128 +143,95 @@ public class EntityTamableFox extends EntityFox { // Wild animal attacking this.targetSelector.a(4, new PathfinderGoalNearestAttackableTarget(this, EntityLiving.class, 10, false, false, - entityLiving -> (!tamed || (Config.doesTamedAttackWildAnimals() && tamed)) && ( + entityLiving -> (!tamed || (Config.doesTamedAttackWildAnimals() && tamed)) && ( entityLiving instanceof EntityChicken || - entityLiving instanceof EntityRabbit || - (entityLiving instanceof EntityTurtle && EntityTurtle.bw.test((EntityLiving) entityLiving)) || - entityLiving instanceof EntityFishSchool))); + entityLiving instanceof EntityRabbit || + (entityLiving instanceof EntityTurtle && EntityTurtle.bw.test((EntityLiving) entityLiving)) || + entityLiving instanceof EntityFishSchool))); } catch (Exception e) { e.printStackTrace(); } } - @Override - protected void initAttributes() { - this.getAttributeMap().b(GenericAttributes.MAX_HEALTH); - this.getAttributeMap().b(GenericAttributes.KNOCKBACK_RESISTANCE); - this.getAttributeMap().b(GenericAttributes.MOVEMENT_SPEED); - this.getAttributeMap().b(GenericAttributes.ARMOR); - this.getAttributeMap().b(GenericAttributes.ARMOR_TOUGHNESS); - this.getAttributeMap().b(GenericAttributes.FOLLOW_RANGE).setValue(16.0D); - this.getAttributeMap().b(GenericAttributes.ATTACK_KNOCKBACK); + private void clearPathFinderGoals() { + Set goalSet = (Set) getPrivateField("d", PathfinderGoalSelector.class, goalSelector); + Set targetSet = (Set) getPrivateField("d", PathfinderGoalSelector.class, targetSelector); + goalSet.clear(); + targetSet.clear(); - this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.30000001192092896D); - this.getAttributeInstance(GenericAttributes.MAX_HEALTH).setValue(24.0D); + Map goalMap = (Map) getPrivateField("c", PathfinderGoalSelector.class, goalSelector); + Map targetMap = (Map) getPrivateField("c", PathfinderGoalSelector.class, targetSelector); + goalMap.clear(); + targetMap.clear(); - this.getAttributeMap().b(GenericAttributes.ATTACK_DAMAGE).setValue(3.0D); - } - - public boolean isOtherFoxFamily(EntityLiving living) { - if (living instanceof EntityTamableFox) { - EntityTamableFox tamableFox = (EntityTamableFox) living; - - return (tamableFox.isTamed() && tamableFox.getOwner().getUniqueID() == this.getOwner().getUniqueID()); - } else { - return false; - } - } - - public void setTamed(boolean tamed) { - this.tamed = tamed; - - // Remove goals that are not needed when named, or defeats the purpose of taming - this.goalSelector.a(goalRandomSitting); - this.goalSelector.a(goalBerryPicking); - this.goalSelector.a(goalFleeSun); - this.goalSelector.a(goalNearestVillage); + EnumSet goalEnumSet = (EnumSet) getPrivateField("f", PathfinderGoalSelector.class, goalSelector); + EnumSet targetEnumSet = (EnumSet) getPrivateField("f", PathfinderGoalSelector.class, targetSelector); + goalEnumSet.clear(); + targetEnumSet.clear(); } public boolean isTamed() { return tamed; } - public void setOwner(EntityLiving entityLiving) { - this.owner = entityLiving; - updateFoxVisual(); + public void setTamed(boolean tamed) { + this.tamed = tamed; + + // Remove goals that are not needed when named, or defeats the purpose of taming + untamedGoals.forEach(goal -> goalSelector.a(goal)); } public EntityLiving getOwner() { + if (Objects.isNull(owner)) { + if (ownerUUID == null) return null; + OfflinePlayer opOwner = TamableFoxes.getPlugin().getServer().getOfflinePlayer(UUID.fromString(ownerUUID.toString())); + if (opOwner.isOnline()) this.owner = (EntityLiving) ((CraftEntity) opOwner).getHandle(); + } return owner; } - public void setOwnerUUID(UUID uuid) { - this.ownerUUID = uuid; - } - - public UUID getOwnerUUID() { - return ownerUUID; - } - - public void setChosenName(String name) { - this.chosenName = name; + public void setOwner(EntityLiving entityLiving) { + this.owner = entityLiving; + this.ownerUUID = entityLiving.getUniqueID(); updateFoxVisual(); } - public String getChosenName() { - return chosenName; + public void setOwnerUUID(UUID ownerUUID) { + this.ownerUUID = ownerUUID; } - public void setMouthItem(ItemStack item) { - item.setCount(1); - setSlot(EnumItemSlot.MAINHAND, item); + public boolean isOtherFoxFamily(EntityLiving living) { + if (living instanceof EntityTamableFox) { + EntityTamableFox tamableFox = (EntityTamableFox) living; - save(); - } - - public void setMouthItem(org.bukkit.inventory.ItemStack item) { - ItemStack itemNMS = CraftItemStack.asNMSCopy(item); - setMouthItem(itemNMS); - } - - public ItemStack getMouthItem() { - return getEquipment(EnumItemSlot.MAINHAND); - } - - public Item dropMouthItem() { - Item droppedItem = getBukkitEntity().getWorld().dropItem(getBukkitEntity().getLocation().add(0, 0, 0), CraftItemStack.asBukkitCopy(getMouthItem())); - setSlot(EnumItemSlot.MAINHAND, new net.minecraft.server.v1_15_R1.ItemStack(Items.AIR)); - - return droppedItem; - } - - @Override - public void setSitting(boolean sit) { - super.setSitting(sit); - - if (sleeping) { - sleeping = false; - super.setSleeping(false); + return (tamableFox.isTamed() && tamableFox.getOwner() != null && tamableFox.getOwner().getUniqueID() == this.getOwner().getUniqueID()); + } else { + return false; } + } + + public void updateFoxVisual() { + new BukkitRunnable() { + @Override + public void run() { + goalSit.setSitting(sitting); + + if (tamed && owner != null && !hasCustomName() && Config.doesShowOwnerFoxName()) { + getBukkitEntity().setCustomName(LanguageConfig.getOwnerInFoxNameFormat().replaceAll("%player%", owner.getName())); + } + } + }.runTask(TamableFoxes.getPlugin()); + } + + public void setHardSitting(boolean hardSitting) { + super.setSitting(hardSitting); + this.sitting = hardSitting; + + if (super.isSleeping()) super.setSleeping(false); updateFoxVisual(); } - public void setHardSitting(boolean sit) { - super.setSitting(sit); - this.sitting = sit; - - if (sleeping) { - sleeping = false; - super.setSleeping(false); - } - - updateFoxVisual(); - } public boolean toggleSitting() { this.sitting = !this.sitting; @@ -242,31 +240,29 @@ public class EntityTamableFox extends EntityFox { return this.sitting; } - public boolean isJumping() { - return this.jumping; + public ItemStack getMouthItem() { + return getEquipment(EnumItemSlot.MAINHAND); } - public void updateFoxVisual() { - new UpdateFoxRunnable().runTask(TamableFoxes.getPlugin()); + public void setMouthItem(ItemStack item) { + item.setCount(1); + setSlot(EnumItemSlot.MAINHAND, item); + saveNbt(); } - private class UpdateFoxRunnable extends BukkitRunnable { - UpdateFoxRunnable() { - - } - - public void run() { - goalSit.setSitting(sitting); - - if (tamed) { - getBukkitEntity().setCustomName((chosenName != null ? chosenName : "") - + (owner != null && Config.doesShowOwnerFoxName() ? ChatColor.RESET + " (" + owner.getName() + ")" : "")); - getBukkitEntity().setCustomNameVisible(Config.doesShowNameTags()); - } - } + public void setMouthItem(org.bukkit.inventory.ItemStack item) { + ItemStack itemNMS = CraftItemStack.asNMSCopy(item); + setMouthItem(itemNMS); } - public void save() { + public org.bukkit.entity.Item dropMouthItem() { + Item droppedItem = getBukkitEntity().getWorld().dropItem(getBukkitEntity().getLocation(), CraftItemStack.asBukkitCopy(getMouthItem())); + setSlot(EnumItemSlot.MAINHAND, new net.minecraft.server.v1_15_R1.ItemStack(Items.AIR)); + + return droppedItem; + } + + public void saveNbt() { NamespacedKey rootKey = new NamespacedKey(TamableFoxes.getPlugin(), "tamableFoxes"); CraftPersistentDataContainer persistentDataContainer = getBukkitEntity().getPersistentDataContainer(); PersistentDataContainer tamableFoxesData; @@ -277,218 +273,10 @@ public class EntityTamableFox extends EntityFox { } NamespacedKey ownerKey = new NamespacedKey(TamableFoxes.getPlugin(), "owner"); - NamespacedKey chosenNameKey = new NamespacedKey(TamableFoxes.getPlugin(), "chosenName"); NamespacedKey sittingKey = new NamespacedKey(TamableFoxes.getPlugin(), "sitting"); - NamespacedKey sleepingKey = new NamespacedKey(TamableFoxes.getPlugin(), "sleeping"); tamableFoxesData.set(ownerKey, PersistentDataType.STRING, getOwner() == null ? "none" : getOwner().getUniqueID().toString()); - if (getChosenName() != null && !getChosenName().isEmpty()) { - tamableFoxesData.set(chosenNameKey, PersistentDataType.STRING, getChosenName()); - } - tamableFoxesData.set(sittingKey, PersistentDataType.BYTE, (byte) (isSitting() ? 1 : 0)); - tamableFoxesData.set(sleepingKey, PersistentDataType.BYTE, (byte) (isSleeping() ? 1 : 0)); + tamableFoxesData.set(sittingKey, PersistentDataType.BYTE, (byte) (isSitting() ? 1 : 0)); persistentDataContainer.set(rootKey, PersistentDataType.TAG_CONTAINER, tamableFoxesData); } - - // Used for all the nasty stuff below. - private static boolean isLevelAtLeast(NBTTagCompound tag, int level) { - return tag.hasKey("Bukkit.updateLevel") && tag.getInt("Bukkit.updateLevel") >= level; - } - - // To remove a call to initializePathFinderGoals() - // This is all from every super class that has a method like this. - // This was needed because you cant call a "super.super.method()" - @Override - public void a(NBTTagCompound nbttagcompound) { - try { - // EntityLiving - this.setAbsorptionHearts(nbttagcompound.getFloat("AbsorptionAmount")); - if (nbttagcompound.hasKeyOfType("Attributes", 9) && this.world != null && !this.world.isClientSide) { - GenericAttributes.a(this.getAttributeMap(), nbttagcompound.getList("Attributes", 10)); - } - - if (nbttagcompound.hasKeyOfType("ActiveEffects", 9)) { - NBTTagList nbttaglist = nbttagcompound.getList("ActiveEffects", 10); - - for(int i = 0; i < nbttaglist.size(); ++i) { - NBTTagCompound nbttagcompound1 = nbttaglist.getCompound(i); - MobEffect mobeffect = MobEffect.b(nbttagcompound1); - if (mobeffect != null) { - this.effects.put(mobeffect.getMobEffect(), mobeffect); - } - } - } - - if (nbttagcompound.hasKey("Bukkit.MaxHealth")) { - NBTBase nbtbase = nbttagcompound.get("Bukkit.MaxHealth"); - if (nbtbase.getTypeId() == 5) { - this.getAttributeInstance(GenericAttributes.MAX_HEALTH).setValue(((NBTTagFloat)nbtbase).asDouble()); - } else if (nbtbase.getTypeId() == 3) { - this.getAttributeInstance(GenericAttributes.MAX_HEALTH).setValue(((NBTTagInt)nbtbase).asDouble()); - } - } - - if (nbttagcompound.hasKeyOfType("Health", 99)) { - this.setHealth(nbttagcompound.getFloat("Health")); - } - - this.hurtTicks = nbttagcompound.getShort("HurtTime"); - this.deathTicks = nbttagcompound.getShort("DeathTime"); - this.hurtTimestamp = nbttagcompound.getInt("HurtByTimestamp"); - if (nbttagcompound.hasKeyOfType("Team", 8)) { - String s = nbttagcompound.getString("Team"); - ScoreboardTeam scoreboardteam = this.world.getScoreboard().getTeam(s); - boolean flag = scoreboardteam != null && this.world.getScoreboard().addPlayerToTeam(this.getUniqueIDString(), scoreboardteam); - if (!flag) { - LOGGER.warn("Unable to add mob to team \"{}\" (that team probably doesn't exist)", s); - } - } - - if (nbttagcompound.getBoolean("FallFlying")) { - this.setFlag(7, true); - } - - if (nbttagcompound.hasKeyOfType("SleepingX", 99) && nbttagcompound.hasKeyOfType("SleepingY", 99) && nbttagcompound.hasKeyOfType("SleepingZ", 99)) { - BlockPosition blockposition = new BlockPosition(nbttagcompound.getInt("SleepingX"), nbttagcompound.getInt("SleepingY"), nbttagcompound.getInt("SleepingZ")); - this.d(blockposition); - this.datawatcher.set(POSE, EntityPose.SLEEPING); - if (!this.justCreated) { - this.a(blockposition); - } - } - - if (nbttagcompound.hasKeyOfType("Brain", 10)) { - this.bo = this.a(new Dynamic(DynamicOpsNBT.a, nbttagcompound.get("Brain"))); - } - // EntityInsentient - NonNullList by = (NonNullList) Utils.getPrivateFieldValue(EntityInsentient.class, "by", this); - NonNullList bx = (NonNullList) Utils.getPrivateFieldValue(EntityInsentient.class, "bx", this); - - boolean data; - if (nbttagcompound.hasKeyOfType("CanPickUpLoot", 1)) { - data = nbttagcompound.getBoolean("CanPickUpLoot"); - if (isLevelAtLeast(nbttagcompound, 1) || data) { - this.setCanPickupLoot(data); - } - } - - data = nbttagcompound.getBoolean("PersistenceRequired"); - if (isLevelAtLeast(nbttagcompound, 1) || data) { - this.persistent = data; - } - - NBTTagList nbttaglist; - int i; - if (nbttagcompound.hasKeyOfType("ArmorItems", 9)) { - nbttaglist = nbttagcompound.getList("ArmorItems", 10); - - for(i = 0; i < by.size(); ++i) { - by.set(i, ItemStack.a(nbttaglist.getCompound(i))); - } - } - - if (nbttagcompound.hasKeyOfType("HandItems", 9)) { - nbttaglist = nbttagcompound.getList("HandItems", 10); - - for(i = 0; i < bx.size(); ++i) { - bx.set(i, ItemStack.a(nbttaglist.getCompound(i))); - } - } - - if (nbttagcompound.hasKeyOfType("ArmorDropChances", 9)) { - nbttaglist = nbttagcompound.getList("ArmorDropChances", 5); - - for(i = 0; i < nbttaglist.size(); ++i) { - this.dropChanceArmor[i] = nbttaglist.i(i); - } - } - - if (nbttagcompound.hasKeyOfType("HandDropChances", 9)) { - nbttaglist = nbttagcompound.getList("HandDropChances", 5); - - for(i = 0; i < nbttaglist.size(); ++i) { - this.dropChanceHand[i] = nbttaglist.i(i); - } - } - - if (nbttagcompound.hasKeyOfType("Leash", 10)) { - //this.bG = nbttagcompound.getCompound("Leash"); - Utils.setPrivateFieldValue(EntityInsentient.class, "bG", this, nbttagcompound.getCompound("Leash")); - } - - this.p(nbttagcompound.getBoolean("LeftHanded")); - if (nbttagcompound.hasKeyOfType("DeathLootTable", 8)) { - this.lootTableKey = new MinecraftKey(nbttagcompound.getString("DeathLootTable")); - this.lootTableSeed = nbttagcompound.getLong("DeathLootTableSeed"); - } - - this.setNoAI(nbttagcompound.getBoolean("NoAI")); - // EntityAgeable - this.setAgeRaw(nbttagcompound.getInt("Age")); - this.c = nbttagcompound.getInt("ForcedAge"); - this.ageLocked = nbttagcompound.getBoolean("AgeLocked"); - // EntityAnimal - this.loveTicks = nbttagcompound.getInt("InLove"); - this.breedCause = nbttagcompound.b("LoveCause") ? nbttagcompound.a("LoveCause") : null; - - NBTTagList foxNBTTagList = nbttagcompound.getList("TrustedUUIDs", 10); - - Method method = this.getClass().getSuperclass().getDeclaredMethod("b", UUID.class); - method.setAccessible(true); - for (int index = 0; index < foxNBTTagList.size(); ++index) { - //this.b(GameProfileSerializer.b(nbttaglist.getCompound(i))); - method.invoke(this, GameProfileSerializer.b(foxNBTTagList.getCompound(index))); - } - method.setAccessible(false); - - this.setSleeping(nbttagcompound.getBoolean("Sleeping")); - this.setFoxType(EntityFox.Type.a(nbttagcompound.getString("Type"))); - - // Use super class due to the new set sitting causing errors - super.setSitting(nbttagcompound.getBoolean("Sitting")); - - this.setCrouching(nbttagcompound.getBoolean("Crouching")); - } catch (Exception e) { - e.printStackTrace(); - } - } - - // To remove the last call to initializePathFinderGoals() - // Cant just override because its a private method - @Override - @Nullable - public GroupDataEntity prepare(GeneratorAccess generatoraccess, DifficultyDamageScaler difficultydamagescaler, - EnumMobSpawn enummobspawn, GroupDataEntity groupdataentity, NBTTagCompound nbttagcompound) { - BiomeBase biomebase = generatoraccess.getBiome(new BlockPosition(this)); - Type entityfox_type = Type.a(biomebase); - boolean flag = false; - if (groupdataentity instanceof i) { - entityfox_type = ((i) groupdataentity).a; - if (((i) groupdataentity).a() >= 2) { - flag = true; - } else { - ((i) groupdataentity).b(); - } - } else { - groupdataentity = new i(entityfox_type); - ((i) groupdataentity).b(); - } - - this.setFoxType(entityfox_type); - if (flag) { - this.setAgeRaw(-24000); - } - - this.initPathfinder(); - this.a(difficultydamagescaler); - this.getAttributeInstance(GenericAttributes.FOLLOW_RANGE).addModifier(new AttributeModifier("Random spawn bonus", - this.random.nextGaussian() * 0.05D, AttributeModifier.Operation.MULTIPLY_BASE)); - if (this.random.nextFloat() < 0.05F) { - this.p(true); - } else { - this.p(false); - } - - return groupdataentity; - } } diff --git a/src/main/java/net/seanomik/tamablefoxes/TamableFoxes.java b/src/main/java/net/seanomik/tamablefoxes/TamableFoxes.java index 16005aa..9fd64ca 100644 --- a/src/main/java/net/seanomik/tamablefoxes/TamableFoxes.java +++ b/src/main/java/net/seanomik/tamablefoxes/TamableFoxes.java @@ -27,13 +27,11 @@ import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.scheduler.BukkitRunnable; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; -import java.util.UUID; import java.util.stream.Collectors; // @TODO: @@ -101,12 +99,12 @@ public final class TamableFoxes extends JavaPlugin implements Listener { @Override public void onDisable() { getServer().getConsoleSender().sendMessage(Utils.getPrefix() + ChatColor.YELLOW + LanguageConfig.getSavingFoxMessage()); - spawnedFoxes.forEach(EntityTamableFox::save); + spawnedFoxes.forEach(EntityTamableFox::saveNbt); } @EventHandler public void onWorldSaveEvent(WorldSaveEvent event) { - spawnedFoxes.forEach(EntityTamableFox::save); + spawnedFoxes.forEach(EntityTamableFox::saveNbt); } @EventHandler @@ -150,68 +148,57 @@ public final class TamableFoxes extends JavaPlugin implements Listener { if (Utils.isTamableFox(entity)) { EntityTamableFox tamableFox = (EntityTamableFox) ((CraftEntity) entity).getHandle(); - // Check if its tamed but ignore it if the player is holding sweet berries for breeding - if (tamableFox.isTamed() && tamableFox.getOwner() != null && itemHand.getType() != Material.SWEET_BERRIES) { + // Check if its tamed but ignore it if the player is holding sweet berries for breeding or nametag for renaming + if (tamableFox.isTamed() && tamableFox.getOwner() != null && itemHand.getType() != Material.SWEET_BERRIES && itemHand.getType() != Material.NAME_TAG) { if (tamableFox.getOwner().getUniqueID() == player.getUniqueId()) { + event.setCancelled(true); if (player.isSneaking()) { net.minecraft.server.v1_15_R1.ItemStack foxMouth = tamableFox.getEquipment(EnumItemSlot.MAINHAND); - - if (foxMouth.isEmpty() && itemHand.getType() != Material.AIR) { // Giving an item + if (!foxMouth.isEmpty()) tamableFox.dropMouthItem(); + if (itemHand.getType() != Material.AIR) { tamableFox.setMouthItem(itemHand); - itemHand.setAmount(itemHand.getAmount() - 1); - } else if (!foxMouth.isEmpty() && itemHand.getType() == Material.AIR) { // Taking the item - tamableFox.dropMouthItem(); - } else if (!foxMouth.isEmpty() && itemHand.getType() != Material.AIR){ // Swapping items - // Drop item - tamableFox.dropMouthItem(); - - // Give item and take one away from player - tamableFox.setMouthItem(itemHand); - itemHand.setAmount(itemHand.getAmount() - 1); + if (itemHand.getAmount() == 1) player.getInventory().removeItem(itemHand); + else itemHand.setAmount(itemHand.getAmount() - 1); } - } else if (itemHand.getType() == Material.NAME_TAG) { - tamableFox.setChosenName(handMeta.getDisplayName()); } else { tamableFox.toggleSitting(); } - - event.setCancelled(true); } } else if (itemHand.getType() == Material.CHICKEN && Config.canPlayerTameFox(player)) { if (Math.random() < 0.33D) { // tamed tamableFox.setTamed(true); tamableFox.setOwner(((CraftPlayer) player).getHandle()); - // store uuid + player.getWorld().spawnParticle(Particle.HEART, entity.getLocation(), 6, 0.5D, 0.5D, 0.5D); - // Name fox + player.sendMessage(ChatColor.RED + ChatColor.BOLD.toString() + LanguageConfig.getTamedMessage()); - player.sendMessage(ChatColor.RED + LanguageConfig.getTamingAskingName()); - tamableFox.setChosenName("???"); - //TamableFoxes.getPlugin().sqLiteSetterGetter.saveFox(tamableFox); + if (Config.askForNameAfterTaming()) { + player.sendMessage(ChatColor.RED + LanguageConfig.getTamingAskingName()); + new AnvilGUI.Builder() + .onComplete((plr, text) -> { // Called when the inventory output slot is clicked + if (!text.equals("")) { + tamableFox.getBukkitEntity().setCustomName(text); + tamableFox.setCustomNameVisible(true); + plr.sendMessage(Utils.getPrefix() + ChatColor.GREEN + LanguageConfig.getTamingChosenPerfect(text)); + tamableFox.saveNbt(); + } - event.setCancelled(true); - new AnvilGUI.Builder() - .onComplete((plr, text) -> { // Called when the inventory output slot is clicked - if(!text.equals("")) { - tamableFox.setChosenName(text); - plr.sendMessage(Utils.getPrefix() + ChatColor.GREEN + LanguageConfig.getTamingChosenPerfect(text)); - tamableFox.save(); - } - - return AnvilGUI.Response.close(); - }) - //.preventClose() // Prevents the inventory from being closed - .text("Fox name") // Sets the text the GUI should start with - .plugin(this) // Set the plugin instance - .open(player); // Opens the GUI for the player provided + return AnvilGUI.Response.close(); + }) + //.preventClose() // Prevents the inventory from being closed + .text("Fox name") // Sets the text the GUI should start with + .plugin(this) // Set the plugin instance + .open(player); // Opens the GUI for the player provided + } } else { // Tame failed player.getWorld().spawnParticle(Particle.SMOKE_NORMAL, entity.getLocation(), 10, 0.3D, 0.3D, 0.3D, 0.15D); } if (!player.getGameMode().equals(GameMode.CREATIVE)) { - itemHand.setAmount(itemHand.getAmount() - 1); + if (itemHand.getAmount() == 1) player.getInventory().removeItem(itemHand); + else itemHand.setAmount(itemHand.getAmount() - 1); } event.setCancelled(true); @@ -248,19 +235,14 @@ public final class TamableFoxes extends JavaPlugin implements Listener { public void onEntityDeathEvent(EntityDeathEvent event) { Entity entity = event.getEntity(); if (!Utils.isTamableFox(entity)) return; // Is the entity a tamable fox? - // Remove the fox from storage spawnedFoxes.remove(entity); - // Notify the owner EntityTamableFox tamableFox = (EntityTamableFox) ((CraftEntity) entity).getHandle(); if (tamableFox.getOwner() != null) { Player owner = ((EntityPlayer) tamableFox.getOwner()).getBukkitEntity(); - owner.sendMessage(Utils.getPrefix() + ChatColor.RED + tamableFox.getChosenName() + " was killed!"); + owner.sendMessage(Utils.getPrefix() + ChatColor.RED + (tamableFox.hasCustomName() ? tamableFox.getBukkitEntity().getCustomName() : "Your fox") + " was killed!"); } - - // Remove the fox from database - //sqLiteSetterGetter.removeFox(tamableFox); } public EntityTamableFox spawnTamableFox(Location loc, EntityFox.Type type) { diff --git a/src/main/java/net/seanomik/tamablefoxes/Utils.java b/src/main/java/net/seanomik/tamablefoxes/Utils.java index 019db07..2676ac9 100644 --- a/src/main/java/net/seanomik/tamablefoxes/Utils.java +++ b/src/main/java/net/seanomik/tamablefoxes/Utils.java @@ -1,15 +1,16 @@ package net.seanomik.tamablefoxes; import net.minecraft.server.v1_15_R1.EntityLiving; -import org.bukkit.*; +import org.bukkit.ChatColor; +import org.bukkit.Chunk; +import org.bukkit.NamespacedKey; +import org.bukkit.OfflinePlayer; import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity; import org.bukkit.craftbukkit.v1_15_R1.persistence.CraftPersistentDataContainer; -import org.bukkit.entity.Entity; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; import java.lang.reflect.Constructor; -import java.lang.reflect.Field; import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -25,35 +26,6 @@ public class Utils { return ChatColor.RED + "[Tamable Foxes] "; } - public static Object getPrivateFieldValue(Class c, String field, Object instance) { - Object value = null; - try { - Field f = c.getDeclaredField(field); - f.setAccessible(true); - value = f.get(instance); - f.setAccessible(false); - } catch (Exception e) { - e.printStackTrace(); - } - - return value; - } - - public static void setPrivateFieldValue(Class c, String field, Object instance, Object value) { - try { - Field f = c.getDeclaredField(field); - f.setAccessible(true); - f.set(instance, value); - f.setAccessible(false); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public static void sendConsoleMessage(String message) { - TamableFoxes.getPlugin().getServer().getConsoleSender().sendMessage(message); - } - public static Class getPrivateInnerClass(Class outer, String innerName) { for (Class declaredClass : outer.getDeclaredClasses()) { if (declaredClass.getSimpleName().equals(innerName)) return declaredClass; @@ -101,35 +73,27 @@ public class Utils { if (persistentDataContainer.has(rootKey, PersistentDataType.TAG_CONTAINER)) { PersistentDataContainer tamableFoxesData = persistentDataContainer.get(rootKey, PersistentDataType.TAG_CONTAINER); NamespacedKey ownerKey = new NamespacedKey(TamableFoxes.getPlugin(), "owner"); - NamespacedKey chosenNameKey = new NamespacedKey(TamableFoxes.getPlugin(), "chosenName"); NamespacedKey sittingKey = new NamespacedKey(TamableFoxes.getPlugin(), "sitting"); - NamespacedKey sleepingKey = new NamespacedKey(TamableFoxes.getPlugin(), "sleeping"); String ownerUUIDString = tamableFoxesData.get(ownerKey, PersistentDataType.STRING); - String chosenName = tamableFoxesData.get(chosenNameKey, PersistentDataType.STRING); boolean sitting = ((byte) 1) == tamableFoxesData.get(sittingKey, PersistentDataType.BYTE); - boolean sleeping = ((byte) 1) == tamableFoxesData.get(sleepingKey, PersistentDataType.BYTE); boolean tamed = false; if (!ownerUUIDString.equals("none")) { tamed = true; - OfflinePlayer owner = TamableFoxes.getPlugin().getServer().getOfflinePlayer(UUID.fromString(ownerUUIDString)); if (owner.isOnline()) { EntityLiving livingOwner = (EntityLiving) ((CraftEntity) owner).getHandle(); tamableFox.setOwner(livingOwner); + } else { + tamableFox.setOwnerUUID(UUID.fromString(ownerUUIDString)); } - - tamableFox.setOwnerUUID(owner.getUniqueId()); tamableFox.setTamed(true); - tamableFox.setChosenName(chosenName); } if (sitting && tamed) { tamableFox.setHardSitting(true); - } else if (sleeping) { - tamableFox.setSleeping(true); - } else { // Avoid the foxes getting stuck sitting down. + } else { tamableFox.setSitting(false); tamableFox.setSleeping(false); } diff --git a/src/main/java/net/seanomik/tamablefoxes/io/Config.java b/src/main/java/net/seanomik/tamablefoxes/io/Config.java index f0bed77..766dc79 100644 --- a/src/main/java/net/seanomik/tamablefoxes/io/Config.java +++ b/src/main/java/net/seanomik/tamablefoxes/io/Config.java @@ -10,10 +10,6 @@ public class Config { return plugin.getConfig().getBoolean("show-owner-in-fox-name"); } - public static boolean doesShowNameTags() { - return plugin.getConfig().getBoolean("show-nametags"); - } - public static boolean doesTamedAttackWildAnimals() { return plugin.getConfig().getBoolean("tamed-behavior.attack-wild-animals"); } @@ -22,4 +18,8 @@ public class Config { return !plugin.getConfig().getBoolean("enable-taming-permission") || (plugin.getConfig().getBoolean("enable-taming-permission") && (player.hasPermission("tamablefoxes.tame") || player.isOp())); } + public static boolean askForNameAfterTaming() { + return plugin.getConfig().getBoolean("ask-for-name-after-taming"); + } + } diff --git a/src/main/java/net/seanomik/tamablefoxes/io/LanguageConfig.java b/src/main/java/net/seanomik/tamablefoxes/io/LanguageConfig.java index 8b225e6..f99ef4d 100644 --- a/src/main/java/net/seanomik/tamablefoxes/io/LanguageConfig.java +++ b/src/main/java/net/seanomik/tamablefoxes/io/LanguageConfig.java @@ -107,6 +107,10 @@ public class LanguageConfig extends YamlConfiguration { return getConfig().getString("taming-chosen-name-perfect").replaceAll("%NAME%", chosen); } + public static String getOwnerInFoxNameFormat() { + return getConfig().getString("owner-in-fox-name-format"); + } + public static String getNoPermMessage() { return getConfig().getString("no-permission"); } diff --git a/src/main/java/net/seanomik/tamablefoxes/versions/version_1_15/command/CommandSpawnTamableFox.java b/src/main/java/net/seanomik/tamablefoxes/versions/version_1_15/command/CommandSpawnTamableFox.java index cf9e93d..2282fe7 100644 --- a/src/main/java/net/seanomik/tamablefoxes/versions/version_1_15/command/CommandSpawnTamableFox.java +++ b/src/main/java/net/seanomik/tamablefoxes/versions/version_1_15/command/CommandSpawnTamableFox.java @@ -41,7 +41,7 @@ public class CommandSpawnTamableFox implements TabExecutor { case "red": try { EntityTamableFox fox = plugin.spawnTamableFox(player.getLocation(), EntityFox.Type.RED); - fox.save(); + fox.saveNbt(); player.sendMessage(Utils.getPrefix() + ChatColor.RESET + LanguageConfig.getSpawnedFoxMessage(EntityFox.Type.RED)); } catch (Exception e) { @@ -52,7 +52,7 @@ public class CommandSpawnTamableFox implements TabExecutor { case "snow": try { EntityTamableFox spawnedFox = plugin.spawnTamableFox(player.getLocation(), EntityFox.Type.SNOW); - spawnedFox.save(); + spawnedFox.saveNbt(); player.sendMessage(Utils.getPrefix() + ChatColor.RESET + LanguageConfig.getSpawnedFoxMessage(EntityFox.Type.SNOW)); } catch (Exception e) { diff --git a/src/main/java/net/seanomik/tamablefoxes/versions/version_1_15/pathfinding/FoxPathfinderGoalPanic.java b/src/main/java/net/seanomik/tamablefoxes/versions/version_1_15/pathfinding/FoxPathfinderGoalPanic.java index c42b2ac..41d5682 100644 --- a/src/main/java/net/seanomik/tamablefoxes/versions/version_1_15/pathfinding/FoxPathfinderGoalPanic.java +++ b/src/main/java/net/seanomik/tamablefoxes/versions/version_1_15/pathfinding/FoxPathfinderGoalPanic.java @@ -16,12 +16,12 @@ public class FoxPathfinderGoalPanic extends PathfinderGoalPanic { public boolean a() { try { - Method eFMethod = EntityFox.class.getDeclaredMethod("eF"); - eFMethod.setAccessible(true); - boolean eF = (boolean) eFMethod.invoke(tamableFox); - eFMethod.setAccessible(false); + Method isDefendingMethod = EntityFox.class.getDeclaredMethod("eF"); + isDefendingMethod.setAccessible(true); + boolean isDefending = (boolean) isDefendingMethod.invoke(tamableFox); + isDefendingMethod.setAccessible(false); - return !tamableFox.isTamed() && !eF && super.a(); + return !tamableFox.isTamed() && !isDefending && super.a(); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 0741882..4c538b5 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,7 +1,7 @@ # Config for Tamable Foxes show-owner-in-fox-name: true -show-nametags: true enable-taming-permission: true +ask-for-name-after-taming: false tamed-behavior: attack-wild-animals: true \ No newline at end of file diff --git a/src/main/resources/language.yml b/src/main/resources/language.yml index c86675c..e475ad9 100644 --- a/src/main/resources/language.yml +++ b/src/main/resources/language.yml @@ -2,17 +2,15 @@ unsupported-mc-version-not-registering: "ERROR: This plugin version only support unsupported-mc-version-disabling: "This plugin version only supports Spigot 1.15.X! Disabling plugin!" success-replaced-entity: "Replaced tamable fox entity!" error-to-replaced-entity: "Failed to replace tamable fox entity!" - saving-foxes-message: "Saving foxes." taming-tamed-message: "You just tamed a wild fox!" taming-asking-for-name-message: "What do you want to call it?" taming-chosen-name-perfect: "%NAME% is perfect!" +owner-in-fox-name-format: "%player%'s Fox" no-permission: "You do not have the permission for this command." only-run-by-player: "Command can only be run from player state!" spawned-fox-message: "Spawned a %TYPE% fox." failed-to-spawn-message: "Failed to spawn fox!" -reloaded-message: "Reloaded" - -created-sql-foxes-database: "Created foxes SQLite database!" \ No newline at end of file +reloaded-message: "Reloaded" \ No newline at end of file