Tried to add 1.20.4 support
This commit is contained in:
parent
6c65256c7f
commit
3fc8db17ce
|
@ -19,7 +19,7 @@
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-snapshots</id>
|
<id>codemc-snapshots</id>
|
||||||
<url>https://repo.codemc.io/repository/maven-snapshots/</url>
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-snapshots</id>
|
<id>codemc-snapshots</id>
|
||||||
<url>https://repo.codemc.io/repository/maven-snapshots/</url>
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-snapshots</id>
|
<id>codemc-snapshots</id>
|
||||||
<url>https://repo.codemc.io/repository/maven-snapshots/</url>
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-snapshots</id>
|
<id>codemc-snapshots</id>
|
||||||
<url>https://repo.codemc.io/repository/maven-snapshots/</url>
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-snapshots</id>
|
<id>codemc-snapshots</id>
|
||||||
<url>https://repo.codemc.io/repository/maven-snapshots/</url>
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-snapshots</id>
|
<id>codemc-snapshots</id>
|
||||||
<url>https://repo.codemc.io/repository/maven-snapshots/</url>
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-snapshots</id>
|
<id>codemc-snapshots</id>
|
||||||
<url>https://repo.codemc.io/repository/maven-snapshots/</url>
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-snapshots</id>
|
<id>codemc-snapshots</id>
|
||||||
<url>https://repo.codemc.io/repository/maven-snapshots/</url>
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-snapshots</id>
|
<id>codemc-snapshots</id>
|
||||||
<url>https://repo.codemc.io/repository/maven-snapshots/</url>
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-snapshots</id>
|
<id>codemc-snapshots</id>
|
||||||
<url>https://repo.codemc.io/repository/maven-snapshots/</url>
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-snapshots</id>
|
<id>codemc-snapshots</id>
|
||||||
<url>https://repo.codemc.io/repository/maven-snapshots/</url>
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-snapshots</id>
|
<id>codemc-snapshots</id>
|
||||||
<url>https://repo.codemc.io/repository/maven-snapshots/</url>
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-snapshots</id>
|
<id>codemc-snapshots</id>
|
||||||
<url>https://repo.codemc.io/repository/maven-snapshots/</url>
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-snapshots</id>
|
<id>codemc-snapshots</id>
|
||||||
<url>https://repo.codemc.io/repository/maven-snapshots/</url>
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-snapshots</id>
|
<id>codemc-snapshots</id>
|
||||||
<url>https://repo.codemc.io/repository/maven-snapshots/</url>
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-snapshots</id>
|
<id>codemc-snapshots</id>
|
||||||
<url>https://repo.codemc.io/repository/maven-snapshots/</url>
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>net.seanomik</groupId>
|
||||||
|
<artifactId>tamablefoxes-parent</artifactId>
|
||||||
|
<version>2.2.11-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>net.md-5</groupId>
|
||||||
|
<artifactId>specialsource-maven-plugin</artifactId>
|
||||||
|
<version>1.2.2</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>remap</goal>
|
||||||
|
</goals>
|
||||||
|
<id>remap-obf</id>
|
||||||
|
<configuration>
|
||||||
|
<srgIn>org.spigotmc:minecraft-server:1.20.4-R0.1-SNAPSHOT:txt:maps-mojang</srgIn>
|
||||||
|
<reverse>true</reverse>
|
||||||
|
<remappedDependencies>org.spigotmc:spigot:1.20.4-R0.1-SNAPSHOT:jar:remapped-mojang</remappedDependencies>
|
||||||
|
<remappedArtifactAttached>true</remappedArtifactAttached>
|
||||||
|
<remappedClassifierName>remapped-obf</remappedClassifierName>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>remap</goal>
|
||||||
|
</goals>
|
||||||
|
<id>remap-spigot</id>
|
||||||
|
<configuration>
|
||||||
|
<inputFile>${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar</inputFile>
|
||||||
|
<srgIn>org.spigotmc:minecraft-server:1.20.4-R0.1-SNAPSHOT:csrg:maps-spigot</srgIn>
|
||||||
|
<remappedDependencies>org.spigotmc:spigot:1.20.4-R0.1-SNAPSHOT:jar:remapped-obf</remappedDependencies>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<artifactId>tamablefoxes_v1_20_R3</artifactId>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>spigot-repo</id>
|
||||||
|
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>codemc-snapshots</id>
|
||||||
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.seanomik</groupId>
|
||||||
|
<artifactId>tamablefoxes-util</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spigotmc</groupId>
|
||||||
|
<artifactId>spigot</artifactId>
|
||||||
|
<version>1.20.4-R0.1-SNAPSHOT</version>
|
||||||
|
<classifier>remapped-mojang</classifier>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.wesjd</groupId>
|
||||||
|
<artifactId>anvilgui</artifactId>
|
||||||
|
<version>1.7.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,624 @@
|
||||||
|
package net.seanomik.tamablefoxes.versions.version_1_20_R3;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import net.minecraft.advancements.CriteriaTriggers;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||||
|
import net.minecraft.network.syncher.EntityDataSerializers;
|
||||||
|
import net.minecraft.network.syncher.SynchedEntityData;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.world.InteractionHand;
|
||||||
|
import net.minecraft.world.InteractionResult;
|
||||||
|
import net.minecraft.world.damagesource.DamageSource;
|
||||||
|
import net.minecraft.world.entity.AgeableMob;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.EquipmentSlot;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.Mob;
|
||||||
|
import net.minecraft.world.entity.TamableAnimal;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||||
|
import net.minecraft.world.entity.ai.goal.AvoidEntityGoal;
|
||||||
|
import net.minecraft.world.entity.ai.goal.Goal;
|
||||||
|
import net.minecraft.world.entity.ai.goal.LeapAtTargetGoal;
|
||||||
|
import net.minecraft.world.entity.ai.goal.RandomStrollGoal;
|
||||||
|
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
|
||||||
|
import net.minecraft.world.entity.animal.AbstractFish;
|
||||||
|
import net.minecraft.world.entity.animal.AbstractSchoolingFish;
|
||||||
|
import net.minecraft.world.entity.animal.Animal;
|
||||||
|
import net.minecraft.world.entity.animal.Chicken;
|
||||||
|
import net.minecraft.world.entity.animal.Fox;
|
||||||
|
import net.minecraft.world.entity.animal.PolarBear;
|
||||||
|
import net.minecraft.world.entity.animal.Rabbit;
|
||||||
|
import net.minecraft.world.entity.animal.Turtle;
|
||||||
|
import net.minecraft.world.entity.animal.Wolf;
|
||||||
|
import net.minecraft.world.entity.animal.horse.AbstractHorse;
|
||||||
|
import net.minecraft.world.entity.monster.Creeper;
|
||||||
|
import net.minecraft.world.entity.monster.Ghast;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.BucketItem;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.Items;
|
||||||
|
import net.minecraft.world.item.NameTagItem;
|
||||||
|
import net.minecraft.world.item.SpawnEggItem;
|
||||||
|
import net.minecraft.world.level.GameRules;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.scores.PlayerTeam;
|
||||||
|
import net.seanomik.tamablefoxes.util.Utils;
|
||||||
|
import net.seanomik.tamablefoxes.util.io.Config;
|
||||||
|
import net.seanomik.tamablefoxes.util.io.LanguageConfig;
|
||||||
|
import net.seanomik.tamablefoxes.util.io.sqlite.SQLiteHelper;
|
||||||
|
import net.seanomik.tamablefoxes.versions.version_1_20_R3.pathfinding.*;
|
||||||
|
import net.wesjd.anvilgui.AnvilGUI;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.GameMode;
|
||||||
|
import org.bukkit.craftbukkit.v1_20_R3.event.CraftEventFactory;
|
||||||
|
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
|
||||||
|
import org.bukkit.event.entity.EntityRegainHealthEvent;
|
||||||
|
|
||||||
|
public class EntityTamableFox extends Fox {
|
||||||
|
|
||||||
|
protected static final EntityDataAccessor<Boolean> tamed;
|
||||||
|
protected static final EntityDataAccessor<Optional<UUID>> ownerUUID;
|
||||||
|
|
||||||
|
//private static final EntityDataAccessor<Byte> bw; // DATA_FLAGS_ID
|
||||||
|
private static final Predicate<Entity> AVOID_PLAYERS; // AVOID_PLAYERS
|
||||||
|
|
||||||
|
static {
|
||||||
|
tamed = SynchedEntityData.defineId(EntityTamableFox.class, EntityDataSerializers.BOOLEAN);
|
||||||
|
ownerUUID = SynchedEntityData.defineId(EntityTamableFox.class, EntityDataSerializers.OPTIONAL_UUID);
|
||||||
|
|
||||||
|
AVOID_PLAYERS = (entity) -> !entity.isCrouching();// && EntitySelector.test(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Goal> untamedGoals;
|
||||||
|
private FoxPathfinderGoalSitWhenOrdered goalSitWhenOrdered;
|
||||||
|
|
||||||
|
private FoxPathfinderGoalSleepWhenOrdered goalSleepWhenOrdered;
|
||||||
|
|
||||||
|
public EntityTamableFox(EntityType<? extends Fox> entitytype, Level world) {
|
||||||
|
super(entitytype, world);
|
||||||
|
|
||||||
|
this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(0.33000001192092896D); // Set movement speed
|
||||||
|
if (isTamed()) {
|
||||||
|
this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(24.0D);
|
||||||
|
this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(3.0D);
|
||||||
|
this.setHealth(this.getMaxHealth());
|
||||||
|
} else {
|
||||||
|
this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(10.0D);
|
||||||
|
this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(2.0D);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setTamed(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerGoals() {
|
||||||
|
try {
|
||||||
|
this.goalSitWhenOrdered = new FoxPathfinderGoalSitWhenOrdered(this);
|
||||||
|
this.goalSelector.addGoal(1, goalSitWhenOrdered);
|
||||||
|
this.goalSleepWhenOrdered = new FoxPathfinderGoalSleepWhenOrdered(this);
|
||||||
|
this.goalSelector.addGoal(1, goalSleepWhenOrdered);
|
||||||
|
|
||||||
|
// For reflection, we must use the non remapped names, since this is done at runtime
|
||||||
|
// and the user will be using a normal spigot jar.
|
||||||
|
|
||||||
|
// Wild animal attacking
|
||||||
|
Field landTargetGoal = this.getClass().getSuperclass().getDeclaredField("ck"); // landTargetGoal
|
||||||
|
landTargetGoal.setAccessible(true);
|
||||||
|
landTargetGoal.set(this, new NearestAttackableTargetGoal(this, Animal.class, 10, false, false, (entityliving) -> {
|
||||||
|
return (!isTamed() || (Config.doesTamedAttackWildAnimals() && isTamed())) && (entityliving instanceof Chicken || entityliving instanceof Rabbit);
|
||||||
|
}));
|
||||||
|
|
||||||
|
Field turtleEggTargetGoal = this.getClass().getSuperclass().getDeclaredField("cl"); // turtleEggTargetGoal
|
||||||
|
turtleEggTargetGoal.setAccessible(true);
|
||||||
|
turtleEggTargetGoal.set(this, new NearestAttackableTargetGoal(this, Turtle.class, 10, false, false, Turtle.BABY_ON_LAND_SELECTOR));
|
||||||
|
|
||||||
|
Field fishTargetGoal = this.getClass().getSuperclass().getDeclaredField("cm"); // fishTargetGoal
|
||||||
|
fishTargetGoal.setAccessible(true);
|
||||||
|
fishTargetGoal.set(this, new NearestAttackableTargetGoal(this, AbstractFish.class, 20, false, false, (entityliving) -> {
|
||||||
|
return (!isTamed() || (Config.doesTamedAttackWildAnimals() && isTamed())) && entityliving instanceof AbstractSchoolingFish;
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.goalSelector.addGoal(0, getFoxInnerPathfinderGoal("g")); // FoxFloatGoal
|
||||||
|
this.goalSelector.addGoal(1, getFoxInnerPathfinderGoal("b")); // FaceplantGoal
|
||||||
|
this.goalSelector.addGoal(2, new FoxPathfinderGoalPanic(this, 2.2D));
|
||||||
|
this.goalSelector.addGoal(2, new FoxPathfinderGoalSleepWithOwner(this));
|
||||||
|
this.goalSelector.addGoal(3, getFoxInnerPathfinderGoal("e", Arrays.asList(1.0D), Arrays.asList(double.class))); // FoxBreedGoal
|
||||||
|
|
||||||
|
this.goalSelector.addGoal(4, new AvoidEntityGoal(this, Player.class, 16.0F, 1.6D, 1.4D, (entityliving) -> {
|
||||||
|
return !isTamed() && AVOID_PLAYERS.test((LivingEntity) entityliving) && !this.isDefending();
|
||||||
|
}));
|
||||||
|
this.goalSelector.addGoal(4, new AvoidEntityGoal(this, Wolf.class, 8.0F, 1.6D, 1.4D, (entityliving) -> {
|
||||||
|
return !((Wolf)entityliving).isTame() && !this.isDefending();
|
||||||
|
}));
|
||||||
|
this.goalSelector.addGoal(4, new AvoidEntityGoal(this, PolarBear.class, 8.0F, 1.6D, 1.4D, (entityliving) -> {
|
||||||
|
return !this.isDefending();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.goalSelector.addGoal(5, getFoxInnerPathfinderGoal("u")); // StalkPreyGoal
|
||||||
|
this.goalSelector.addGoal(6, new FoxPounceGoal());
|
||||||
|
this.goalSelector.addGoal(7, getFoxInnerPathfinderGoal("l", Arrays.asList(1.2000000476837158D, true), Arrays.asList(double.class, boolean.class))); // FoxMeleeAttackGoal
|
||||||
|
this.goalSelector.addGoal(8, getFoxInnerPathfinderGoal("h", Arrays.asList(this, 1.25D), Arrays.asList(Fox.class, double.class))); // FoxFollowParentGoal
|
||||||
|
//this.goalSelector.addGoal(8, new FoxPathfinderGoalSleepWithOwner(this));
|
||||||
|
this.goalSelector.addGoal(9, new FoxPathfinderGoalFollowOwner(this, 1.3D, 10.0F, 2.0F, false));
|
||||||
|
this.goalSelector.addGoal(10, new LeapAtTargetGoal(this, 0.4F));
|
||||||
|
this.goalSelector.addGoal(11, new RandomStrollGoal(this, 1.0D));
|
||||||
|
this.goalSelector.addGoal(11, getFoxInnerPathfinderGoal("p")); // FoxSearchForItemsGoal
|
||||||
|
this.goalSelector.addGoal(12, getFoxInnerPathfinderGoal("j", Arrays.asList(this, Player.class, 24.0f),
|
||||||
|
Arrays.asList(Mob.class, Class.class, float.class))); // LookAtPlayer
|
||||||
|
|
||||||
|
this.targetSelector.addGoal(1, new FoxPathfinderGoalOwnerHurtByTarget(this));
|
||||||
|
this.targetSelector.addGoal(2, new FoxPathfinderGoalOwnerHurtTarget(this));
|
||||||
|
this.targetSelector.addGoal(3, (new FoxPathfinderGoalHurtByTarget(this)).setAlertOthers(new Class[0]));
|
||||||
|
|
||||||
|
// Assign all the untamed goals that will later be removed.
|
||||||
|
untamedGoals = new ArrayList<>();
|
||||||
|
|
||||||
|
// SleepGoal
|
||||||
|
Goal sleep = getFoxInnerPathfinderGoal("t");
|
||||||
|
this.goalSelector.addGoal(7, sleep);
|
||||||
|
untamedGoals.add(sleep);
|
||||||
|
|
||||||
|
// PerchAndSearchGoal
|
||||||
|
Goal perchAndSearch = getFoxInnerPathfinderGoal("r");
|
||||||
|
this.goalSelector.addGoal(13, perchAndSearch);
|
||||||
|
untamedGoals.add(perchAndSearch);
|
||||||
|
|
||||||
|
Goal eatBerries = new FoxEatBerriesGoal(1.2000000476837158D, 12, 2);
|
||||||
|
this.goalSelector.addGoal(11, eatBerries);
|
||||||
|
untamedGoals.add(eatBerries); // Maybe this should be configurable too?
|
||||||
|
|
||||||
|
// SeekShelterGoal
|
||||||
|
Goal seekShelter = getFoxInnerPathfinderGoal("s", Arrays.asList(1.25D), Arrays.asList(double.class));
|
||||||
|
this.goalSelector.addGoal(6, seekShelter);
|
||||||
|
untamedGoals.add(seekShelter);
|
||||||
|
|
||||||
|
// FoxStrollThroughVillageGoal
|
||||||
|
Goal strollThroughVillage = getFoxInnerPathfinderGoal("q", Arrays.asList(32, 200), Arrays.asList(int.class, int.class));
|
||||||
|
this.goalSelector.addGoal(9, strollThroughVillage);
|
||||||
|
untamedGoals.add(strollThroughVillage);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected EntityDataAccessor<Byte> getDataFlagsId() throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
Field dataFlagsField = Fox.class.getDeclaredField("bY"); // DATA_FLAGS_ID
|
||||||
|
dataFlagsField.setAccessible(true);
|
||||||
|
EntityDataAccessor<Byte> dataFlagsId = (EntityDataAccessor<Byte>) dataFlagsField.get(null);
|
||||||
|
dataFlagsField.setAccessible(false);
|
||||||
|
|
||||||
|
return dataFlagsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean getFlag(int i) {
|
||||||
|
try {
|
||||||
|
EntityDataAccessor<Byte> dataFlagsId = getDataFlagsId();
|
||||||
|
|
||||||
|
return ((Byte)super.entityData.get(dataFlagsId) & i) != 0;
|
||||||
|
} catch (IllegalAccessException | NoSuchFieldException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setFlag(int i, boolean flag) {
|
||||||
|
try {
|
||||||
|
EntityDataAccessor<Byte> dataFlagsId = getDataFlagsId();
|
||||||
|
|
||||||
|
if (flag) {
|
||||||
|
this.entityData.set(dataFlagsId, (byte)((Byte)this.entityData.get(dataFlagsId) | i));
|
||||||
|
} else {
|
||||||
|
this.entityData.set(dataFlagsId, (byte)((Byte)this.entityData.get(dataFlagsId) & ~i));
|
||||||
|
}
|
||||||
|
} catch (IllegalAccessException | NoSuchFieldException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDefending() {
|
||||||
|
return getFlag(128);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefending(boolean defending) {
|
||||||
|
setFlag(128, defending);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void defineSynchedData() {
|
||||||
|
super.defineSynchedData();
|
||||||
|
this.entityData.define(tamed, false);
|
||||||
|
this.entityData.define(ownerUUID, Optional.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAdditionalSaveData(CompoundTag compound) {
|
||||||
|
super.addAdditionalSaveData(compound);
|
||||||
|
if (this.getOwnerUUID() == null) {
|
||||||
|
compound.putUUID("OwnerUUID", new UUID(0L, 0L));
|
||||||
|
} else {
|
||||||
|
compound.putUUID("OwnerUUID", this.getOwnerUUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
compound.putBoolean("Sitting", this.goalSitWhenOrdered.isOrderedToSit());
|
||||||
|
compound.putBoolean("Sleeping", this.goalSleepWhenOrdered.isOrderedToSleep());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readAdditionalSaveData(CompoundTag compound) {
|
||||||
|
super.readAdditionalSaveData(compound);
|
||||||
|
UUID ownerUuid = null;
|
||||||
|
|
||||||
|
if (compound.contains("OwnerUUID")) {
|
||||||
|
try {
|
||||||
|
ownerUuid = compound.getUUID("OwnerUUID");
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
String uuidStr = compound.getString("OwnerUUID");
|
||||||
|
if (!uuidStr.isEmpty()) {
|
||||||
|
ownerUuid = UUID.fromString(uuidStr);
|
||||||
|
} else {
|
||||||
|
ownerUuid = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ownerUuid != null && !ownerUuid.equals(new UUID(0, 0))) {
|
||||||
|
this.setOwnerUUID(ownerUuid);
|
||||||
|
this.setTamed(true);
|
||||||
|
} else {
|
||||||
|
this.setTamed(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.goalSitWhenOrdered != null) {
|
||||||
|
this.goalSitWhenOrdered.setOrderedToSit(compound.getBoolean("Sitting"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.goalSleepWhenOrdered != null) {
|
||||||
|
this.goalSleepWhenOrdered.setOrderedToSleep(compound.getBoolean("Sleeping"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isTamed()) {
|
||||||
|
goalSitWhenOrdered.setOrderedToSit(false);
|
||||||
|
goalSleepWhenOrdered.setOrderedToSleep(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTamed() {
|
||||||
|
UUID ownerUuid = getOwnerUUID();
|
||||||
|
return this.entityData.get(tamed) && (ownerUuid != null && !ownerUuid.equals(new UUID(0, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTamed(boolean tamed) {
|
||||||
|
this.entityData.set(EntityTamableFox.tamed, tamed);
|
||||||
|
this.reassessTameGoals();
|
||||||
|
|
||||||
|
if (tamed) {
|
||||||
|
this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(24.0D);
|
||||||
|
this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(3.0D);
|
||||||
|
} else {
|
||||||
|
this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(10.0D);
|
||||||
|
this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(2.0D);
|
||||||
|
}
|
||||||
|
this.setHealth(this.getMaxHealth());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove untamed goals if its tamed.
|
||||||
|
private void reassessTameGoals() {
|
||||||
|
if (!isTamed()) return;
|
||||||
|
|
||||||
|
for (Goal untamedGoal : untamedGoals) {
|
||||||
|
this.goalSelector.removeGoal(untamedGoal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rename(org.bukkit.entity.Player player) {
|
||||||
|
new AnvilGUI.Builder()
|
||||||
|
.onClick((slot, stateSnapshot) -> {
|
||||||
|
String text = stateSnapshot.getText();
|
||||||
|
if (slot == AnvilGUI.Slot.OUTPUT && !text.isEmpty()) {
|
||||||
|
org.bukkit.entity.Entity tamableFox = this.getBukkitEntity();
|
||||||
|
|
||||||
|
// This will auto format the name for config settings.
|
||||||
|
String foxName = LanguageConfig.getFoxNameFormat(text, player.getDisplayName());
|
||||||
|
|
||||||
|
tamableFox.setCustomName(foxName);
|
||||||
|
tamableFox.setCustomNameVisible(true);
|
||||||
|
if (!LanguageConfig.getTamingChosenPerfect(text).equalsIgnoreCase("disabled")) {
|
||||||
|
stateSnapshot.getPlayer().sendMessage(Config.getPrefix() + ChatColor.GREEN + LanguageConfig.getTamingChosenPerfect(text));
|
||||||
|
}
|
||||||
|
} else if (!LanguageConfig.getTamingChosenPerfect(text).equalsIgnoreCase("disabled")) {
|
||||||
|
stateSnapshot.getPlayer().sendMessage(Config.getPrefix() + ChatColor.GRAY + "The fox was not named");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Arrays.asList(AnvilGUI.ResponseAction.close());
|
||||||
|
})
|
||||||
|
.text("Fox name")
|
||||||
|
.title("Name your new friend!")
|
||||||
|
.plugin(Utils.tamableFoxesPlugin)
|
||||||
|
.open(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InteractionResult mobInteract(Player entityhuman, InteractionHand enumhand) {
|
||||||
|
ItemStack itemstack = entityhuman.getItemInHand(enumhand);
|
||||||
|
Item item = itemstack.getItem();
|
||||||
|
|
||||||
|
if (itemstack.getItem() instanceof SpawnEggItem) {
|
||||||
|
return super.mobInteract(entityhuman, enumhand);
|
||||||
|
} else {
|
||||||
|
if (this.isTamed()) {
|
||||||
|
|
||||||
|
// Heal the fox if its health is below the max.
|
||||||
|
if (item.isEdible() && item.getFoodProperties().isMeat() && this.getHealth() < this.getMaxHealth()) {
|
||||||
|
// Only remove the item from the player if they're in survival mode.
|
||||||
|
org.bukkit.entity.Player player = (org.bukkit.entity.Player) entityhuman.getBukkitEntity();
|
||||||
|
if (player.getGameMode() != GameMode.CREATIVE ) {
|
||||||
|
itemstack.shrink(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.heal((float)item.getFoodProperties().getNutrition(), EntityRegainHealthEvent.RegainReason.EATING);
|
||||||
|
return InteractionResult.CONSUME;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOwnedBy(entityhuman) && enumhand == InteractionHand.MAIN_HAND) {
|
||||||
|
// This super method checks if the fox can breed or not.
|
||||||
|
InteractionResult flag = super.mobInteract(entityhuman, enumhand);
|
||||||
|
|
||||||
|
// If the player is not sneaking and the fox cannot breed, then make the fox sit.
|
||||||
|
// @TODO: Do I need to use this.eQ() instead of flag != EnumInteractionResult.SUCCESS?
|
||||||
|
if (!entityhuman.isCrouching() && (flag != InteractionResult.SUCCESS || this.isBaby())) {
|
||||||
|
// Show the rename menu again when trying to use a nametag on the fox.
|
||||||
|
if (itemstack.getItem() instanceof NameTagItem) {
|
||||||
|
org.bukkit.entity.Player player = (org.bukkit.entity.Player) entityhuman.getBukkitEntity();
|
||||||
|
rename(player);
|
||||||
|
return InteractionResult.PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.goalSleepWhenOrdered.setOrderedToSleep(false);
|
||||||
|
this.goalSitWhenOrdered.setOrderedToSit(!this.isOrderedToSit());
|
||||||
|
return InteractionResult.SUCCESS;
|
||||||
|
} else if (entityhuman.isCrouching()) { // Swap/Put/Take item from fox.
|
||||||
|
// Ignore buckets since they can be easily duplicated.
|
||||||
|
if (itemstack.getItem() instanceof BucketItem) {
|
||||||
|
return InteractionResult.PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the fox has something in its mouth and the player has something in its hand, empty it.
|
||||||
|
if (this.hasItemInSlot(EquipmentSlot.MAINHAND)) {
|
||||||
|
getBukkitEntity().getWorld().dropItem(getBukkitEntity().getLocation(), CraftItemStack.asBukkitCopy(this.getItemBySlot(EquipmentSlot.MAINHAND)));
|
||||||
|
this.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.AIR), false);
|
||||||
|
} // Check if the player's hand is empty and if it is, make the fox sleep.
|
||||||
|
// The reason its here is to make sure that we don't take the item
|
||||||
|
// from its mouth and make it sleep in a single click.
|
||||||
|
else if (!entityhuman.hasItemInSlot(EquipmentSlot.MAINHAND)) {
|
||||||
|
this.goalSitWhenOrdered.setOrderedToSit(false);
|
||||||
|
this.goalSleepWhenOrdered.setOrderedToSleep(!this.goalSleepWhenOrdered.isOrderedToSleep());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run this task async to make sure to not slow the server down.
|
||||||
|
// This is needed due to the item being removed as soon as its put in the foxes mouth.
|
||||||
|
Bukkit.getScheduler().runTaskLaterAsynchronously(Utils.tamableFoxesPlugin, ()-> {
|
||||||
|
// Put item in mouth
|
||||||
|
if (entityhuman.hasItemInSlot(EquipmentSlot.MAINHAND)) {
|
||||||
|
ItemStack c = itemstack.copy();
|
||||||
|
c.setCount(1);
|
||||||
|
|
||||||
|
// Only remove the item from the player if they're in survival mode.
|
||||||
|
org.bukkit.entity.Player player = (org.bukkit.entity.Player) entityhuman.getBukkitEntity();
|
||||||
|
if (player.getGameMode() != GameMode.CREATIVE ) {
|
||||||
|
itemstack.shrink(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setItemSlot(EquipmentSlot.MAINHAND, c, false);
|
||||||
|
}
|
||||||
|
}, 1L);
|
||||||
|
|
||||||
|
return InteractionResult.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (item == Items.CHICKEN) {
|
||||||
|
// Check if the player has permissions to tame the fox
|
||||||
|
if (Config.canPlayerTameFox((org.bukkit.entity.Player) entityhuman.getBukkitEntity())) {
|
||||||
|
// Only remove the item from the player if they're in survival mode.
|
||||||
|
org.bukkit.entity.Player player = (org.bukkit.entity.Player) entityhuman.getBukkitEntity();
|
||||||
|
if (player.getGameMode() != GameMode.CREATIVE ) {
|
||||||
|
itemstack.shrink(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLiteHelper sqLiteHelper = SQLiteHelper.getInstance(Utils.tamableFoxesPlugin);
|
||||||
|
int maxTameCount = Config.getMaxPlayerFoxTames();
|
||||||
|
if ( !((org.bukkit.entity.Player) entityhuman.getBukkitEntity()).hasPermission("tamablefoxes.tame.unlimited") && maxTameCount > 0 && sqLiteHelper.getPlayerFoxAmount(entityhuman.getUUID()) >= maxTameCount) {
|
||||||
|
if (!LanguageConfig.getFoxDoesntTrust().equalsIgnoreCase("disabled")) {
|
||||||
|
((org.bukkit.entity.Player) entityhuman.getBukkitEntity()).sendMessage(Config.getPrefix() + ChatColor.RED + LanguageConfig.getFoxDoesntTrust());
|
||||||
|
}
|
||||||
|
|
||||||
|
return InteractionResult.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0.33% chance to tame the fox, also check if the called tame entity event is cancelled or not.
|
||||||
|
if (this.getRandom().nextInt(3) == 0 && !CraftEventFactory.callEntityTameEvent(this, entityhuman).isCancelled()) {
|
||||||
|
this.tame(entityhuman);
|
||||||
|
|
||||||
|
this.navigation.stop();
|
||||||
|
this.goalSitWhenOrdered.setOrderedToSit(true);
|
||||||
|
|
||||||
|
if (maxTameCount > 0) {
|
||||||
|
sqLiteHelper.addPlayerFoxAmount(entityhuman.getUUID(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
getBukkitEntity().getWorld().spawnParticle(org.bukkit.Particle.HEART, getBukkitEntity().getLocation(), 6, 0.5D, 0.5D, 0.5D);
|
||||||
|
|
||||||
|
// Give player tamed message.
|
||||||
|
if (!LanguageConfig.getTamedMessage().equalsIgnoreCase("disabled")) {
|
||||||
|
((org.bukkit.entity.Player) entityhuman.getBukkitEntity()).sendMessage(Config.getPrefix() + ChatColor.GREEN + LanguageConfig.getTamedMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let the player choose the new fox's name if its enabled in config.
|
||||||
|
if (Config.askForNameAfterTaming()) {
|
||||||
|
if (!LanguageConfig.getTamingAskingName().equalsIgnoreCase("disabled")) {
|
||||||
|
player.sendMessage(Config.getPrefix() + ChatColor.RED + LanguageConfig.getTamingAskingName());
|
||||||
|
}
|
||||||
|
rename(player);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
getBukkitEntity().getWorld().spawnParticle(org.bukkit.Particle.SMOKE_NORMAL, getBukkitEntity().getLocation(), 10, 0.2D, 0.2D, 0.2D, 0.15D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return InteractionResult.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.mobInteract(entityhuman, enumhand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityTamableFox getBreedOffspring(ServerLevel worldserver, AgeableMob entityageable) {
|
||||||
|
EntityTamableFox entityfox = (EntityTamableFox) EntityType.FOX.create(worldserver);
|
||||||
|
entityfox.setVariant(this.getRandom().nextBoolean() ? this.getVariant() : ((Fox)entityageable).getVariant());
|
||||||
|
|
||||||
|
UUID uuid = this.getOwnerUUID();
|
||||||
|
if (uuid != null) {
|
||||||
|
entityfox.setOwnerUUID(uuid);
|
||||||
|
entityfox.setTamed(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entityfox;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public UUID getOwnerUUID() {
|
||||||
|
return (UUID) ((Optional) this.entityData.get(ownerUUID)).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOwnerUUID(@Nullable UUID ownerUuid) {
|
||||||
|
this.entityData.set(ownerUUID, Optional.ofNullable(ownerUuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tame(Player owner) {
|
||||||
|
this.setTamed(true);
|
||||||
|
this.setOwnerUUID(owner.getUUID());
|
||||||
|
|
||||||
|
// Give the player the taming advancement.
|
||||||
|
if (owner instanceof ServerPlayer) {
|
||||||
|
CriteriaTriggers.TAME_ANIMAL.trigger((ServerPlayer) owner, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public LivingEntity getOwner() {
|
||||||
|
try {
|
||||||
|
UUID ownerUuid = this.getOwnerUUID();
|
||||||
|
return ownerUuid == null ? null : this.getCommandSenderWorld().getPlayerByUUID(ownerUuid);
|
||||||
|
} catch (IllegalArgumentException var2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only attack entity if its not attacking owner.
|
||||||
|
@Override
|
||||||
|
public boolean canAttack(LivingEntity entity) {
|
||||||
|
return !this.isOwnedBy(entity) && super.canAttack(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOwnedBy(LivingEntity entity) {
|
||||||
|
return entity == this.getOwner();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
deobf: wantsToAttack (Copied from EntityWolf)
|
||||||
|
This code being from EntityWolf also means that wolves will want to attack foxes
|
||||||
|
Our life would be so much easier if we could extend both EntityFox and EntityTameableAnimal
|
||||||
|
*/
|
||||||
|
public boolean wantsToAttack(LivingEntity entityliving, LivingEntity entityliving1) {
|
||||||
|
if (!(entityliving instanceof Creeper) && !(entityliving instanceof Ghast)) {
|
||||||
|
if (entityliving instanceof EntityTamableFox) {
|
||||||
|
EntityTamableFox entityFox = (EntityTamableFox) entityliving;
|
||||||
|
return !entityFox.isTamed() || entityFox.getOwner() != entityliving1;
|
||||||
|
} else {
|
||||||
|
return (!(entityliving instanceof Player)
|
||||||
|
|| !(entityliving1 instanceof Player) ||
|
||||||
|
((Player) entityliving1).canHarmPlayer((Player) entityliving)) && ((!(entityliving instanceof AbstractHorse)
|
||||||
|
|| !((AbstractHorse) entityliving).isTamed()) && (!(entityliving instanceof TamableAnimal)
|
||||||
|
|| !((TamableAnimal) entityliving).isTame()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the scoreboard team to the same as the owner if its tamed.
|
||||||
|
@Override
|
||||||
|
public PlayerTeam getTeam() {
|
||||||
|
if (this.isTamed()) {
|
||||||
|
LivingEntity var0 = this.getOwner();
|
||||||
|
if (var0 != null) {
|
||||||
|
return var0.getTeam();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getTeam();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override isAlliedTo (Entity::r(Entity))
|
||||||
|
@Override
|
||||||
|
public boolean isAlliedTo(Entity entity) {
|
||||||
|
if (this.isTamed()) {
|
||||||
|
LivingEntity entityOwner = this.getOwner();
|
||||||
|
if (entity == entityOwner) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entityOwner != null) {
|
||||||
|
return entityOwner.isAlliedTo(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.isAlliedTo(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the fox dies, show a chat message, and remove the player's stored tamed foxed.
|
||||||
|
@Override
|
||||||
|
public void die(DamageSource damageSource) {
|
||||||
|
if (!this.getCommandSenderWorld().isClientSide && this.getCommandSenderWorld().getGameRules().getBoolean(GameRules.RULE_SHOWDEATHMESSAGES) && this.getOwner() instanceof ServerPlayer) {
|
||||||
|
//this.getOwner().sendMessage(this.getCombatTracker().getDeathMessage(), getOwnerUUID());
|
||||||
|
this.getOwner().sendSystemMessage(this.getCombatTracker().getDeathMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the amount of foxes the player has tamed if the limit is enabled.
|
||||||
|
if (Config.getMaxPlayerFoxTames() > 0 && this.getOwner() != null) {
|
||||||
|
SQLiteHelper sqliteHelper = SQLiteHelper.getInstance(Utils.tamableFoxesPlugin);
|
||||||
|
sqliteHelper.removePlayerFoxAmount(this.getOwner().getUUID(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.die(damageSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Goal getFoxInnerPathfinderGoal(String innerName, List<Object> args, List<Class<?>> argTypes) {
|
||||||
|
return (Goal) Utils.instantiatePrivateInnerClass(Fox.class, innerName, this, args, argTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Goal getFoxInnerPathfinderGoal(String innerName) {
|
||||||
|
return (Goal) Utils.instantiatePrivateInnerClass(Fox.class, innerName, this, Arrays.asList(), Arrays.asList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOrderedToSit() { return this.goalSitWhenOrdered.isOrderedToSit(); }
|
||||||
|
|
||||||
|
public void setOrderedToSit(boolean flag) { this.goalSitWhenOrdered.setOrderedToSit(flag); }
|
||||||
|
|
||||||
|
public boolean isOrderedToSleep() { return this.goalSleepWhenOrdered.isOrderedToSleep(); }
|
||||||
|
|
||||||
|
public void setOrderedToSleep(boolean flag) { this.goalSleepWhenOrdered.setOrderedToSleep(flag); }
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package net.seanomik.tamablefoxes.versions.version_1_20_R3;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.UUID;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.animal.Fox;
|
||||||
|
import net.seanomik.tamablefoxes.util.FieldHelper;
|
||||||
|
import net.seanomik.tamablefoxes.util.NMSInterface;
|
||||||
|
import net.seanomik.tamablefoxes.util.io.Config;
|
||||||
|
import net.seanomik.tamablefoxes.util.io.LanguageConfig;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftEntity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public class NMSInterface_1_20_R3 implements NMSInterface {
|
||||||
|
@Override
|
||||||
|
public void registerCustomFoxEntity() {
|
||||||
|
try { // Replace the fox entity
|
||||||
|
Field field = EntityType.FOX.getClass().getDeclaredField("bz"); // bz = factory
|
||||||
|
FieldHelper.setFieldUsingUnsafe(field, EntityType.FOX, (EntityType.EntityFactory<Fox>) EntityTamableFox::new);
|
||||||
|
Bukkit.getServer().getConsoleSender().sendMessage(Config.getPrefix() + ChatColor.GREEN + LanguageConfig.getSuccessReplaced());
|
||||||
|
} catch (Exception e) {
|
||||||
|
Bukkit.getServer().getConsoleSender().sendMessage(Config.getPrefix() + ChatColor.RED + LanguageConfig.getFailureReplace());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void spawnTamableFox(Location loc, FoxType type) {
|
||||||
|
EntityTamableFox tamableFox = (EntityTamableFox) ((CraftEntity) loc.getWorld().spawnEntity(loc, org.bukkit.entity.EntityType.FOX)).getHandle();
|
||||||
|
tamableFox.setVariant((type == FoxType.RED) ? Fox.Type.RED : Fox.Type.SNOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changeFoxOwner(org.bukkit.entity.Fox fox, Player newOwner) {
|
||||||
|
EntityTamableFox tamableFox = (EntityTamableFox) ((CraftEntity) fox).getHandle();
|
||||||
|
tamableFox.setOwnerUUID(newOwner.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID getFoxOwner(org.bukkit.entity.Fox fox) {
|
||||||
|
EntityTamableFox tamableFox = (EntityTamableFox) ((CraftEntity) fox).getHandle();
|
||||||
|
return tamableFox.getOwnerUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renameFox(org.bukkit.entity.Fox fox, Player player) {
|
||||||
|
EntityTamableFox tamableFox = (EntityTamableFox) ((CraftEntity) fox).getHandle();
|
||||||
|
tamableFox.rename(player);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,151 @@
|
||||||
|
package net.seanomik.tamablefoxes.versions.version_1_20_R3.pathfinding;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.ai.goal.Goal;
|
||||||
|
import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation;
|
||||||
|
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
|
||||||
|
import net.minecraft.world.entity.ai.navigation.PathNavigation;
|
||||||
|
import net.minecraft.world.level.LevelReader;
|
||||||
|
import net.minecraft.world.level.block.LeavesBlock;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.pathfinder.BlockPathTypes;
|
||||||
|
import net.minecraft.world.level.pathfinder.WalkNodeEvaluator;
|
||||||
|
import net.seanomik.tamablefoxes.versions.version_1_20_R3.EntityTamableFox;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftEntity;
|
||||||
|
import org.bukkit.event.entity.EntityTeleportEvent;
|
||||||
|
|
||||||
|
public class FoxPathfinderGoalFollowOwner extends Goal {
|
||||||
|
public static final int TELEPORT_WHEN_DISTANCE_IS = 12;
|
||||||
|
private static final int MIN_HORIZONTAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING = 2;
|
||||||
|
private static final int MAX_HORIZONTAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING = 3;
|
||||||
|
private static final int MAX_VERTICAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING = 1;
|
||||||
|
private final EntityTamableFox tamableFox;
|
||||||
|
private LivingEntity owner;
|
||||||
|
private final LevelReader level;
|
||||||
|
private final double speedModifier;
|
||||||
|
private final PathNavigation navigation;
|
||||||
|
private int timeToRecalcPath;
|
||||||
|
private final float stopDistance;
|
||||||
|
private final float startDistance;
|
||||||
|
private float oldWaterCost;
|
||||||
|
private final boolean canFly;
|
||||||
|
|
||||||
|
public FoxPathfinderGoalFollowOwner(EntityTamableFox entityfox, double d0, float f, float f1, boolean flag) {
|
||||||
|
this.tamableFox = entityfox;
|
||||||
|
this.level = entityfox.level();
|
||||||
|
this.speedModifier = d0;
|
||||||
|
this.navigation = entityfox.getNavigation();
|
||||||
|
this.startDistance = f;
|
||||||
|
this.stopDistance = f1;
|
||||||
|
this.canFly = flag;
|
||||||
|
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
|
||||||
|
if (!(entityfox.getNavigation() instanceof GroundPathNavigation) && !(entityfox.getNavigation() instanceof FlyingPathNavigation)) {
|
||||||
|
throw new IllegalArgumentException("Unsupported mob type for FoxPathfinderGoalFollowOwner");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canUse() {
|
||||||
|
LivingEntity entityliving = this.tamableFox.getOwner();
|
||||||
|
if (entityliving == null) {
|
||||||
|
return false;
|
||||||
|
} else if (entityliving.isSpectator()) {
|
||||||
|
return false;
|
||||||
|
} else if (this.tamableFox.isOrderedToSit() || this.tamableFox.isOrderedToSleep()) {
|
||||||
|
return false;
|
||||||
|
} else if (this.tamableFox.distanceToSqr(entityliving) < (double)(this.startDistance * this.startDistance)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
this.owner = entityliving;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canContinueToUse() {
|
||||||
|
return !this.navigation.isDone() && (!this.tamableFox.isOrderedToSit() && !this.tamableFox.isOrderedToSleep() && this.tamableFox.distanceToSqr(this.owner) > (double) (this.stopDistance * this.stopDistance));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
this.timeToRecalcPath = 0;
|
||||||
|
this.oldWaterCost = this.tamableFox.getPathfindingMalus(BlockPathTypes.WATER);
|
||||||
|
this.tamableFox.setPathfindingMalus(BlockPathTypes.WATER, 0.0F);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
this.owner = null;
|
||||||
|
this.navigation.stop();
|
||||||
|
this.tamableFox.setPathfindingMalus(BlockPathTypes.WATER, this.oldWaterCost);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tick() {
|
||||||
|
this.tamableFox.getLookControl().setLookAt(this.owner, 10.0F, (float)this.tamableFox.getMaxHeadXRot());
|
||||||
|
if (--this.timeToRecalcPath <= 0) {
|
||||||
|
this.timeToRecalcPath = 10;
|
||||||
|
if (!this.tamableFox.isLeashed() && !this.tamableFox.isPassenger()) {
|
||||||
|
if (this.tamableFox.distanceToSqr(this.owner) >= 144.0D) {
|
||||||
|
this.teleportToOwner();
|
||||||
|
} else {
|
||||||
|
this.navigation.moveTo(this.owner, this.speedModifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void teleportToOwner() {
|
||||||
|
BlockPos blockposition = this.owner.blockPosition();
|
||||||
|
|
||||||
|
for(int i = 0; i < 10; ++i) {
|
||||||
|
int j = this.randomIntInclusive(-3, 3);
|
||||||
|
int k = this.randomIntInclusive(-1, 1);
|
||||||
|
int l = this.randomIntInclusive(-3, 3);
|
||||||
|
boolean flag = this.maybeTeleportTo(blockposition.getX() + j, blockposition.getY() + k, blockposition.getZ() + l);
|
||||||
|
if (flag) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean maybeTeleportTo(int i, int j, int k) {
|
||||||
|
if (Math.abs((double)i - this.owner.getX()) < 2.0D && Math.abs((double)k - this.owner.getZ()) < 2.0D) {
|
||||||
|
return false;
|
||||||
|
} else if (!this.canTeleportTo(new BlockPos(i, j, k))) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
CraftEntity entity = this.tamableFox.getBukkitEntity();
|
||||||
|
Location to = new Location(entity.getWorld(), (double)i + 0.5D, (double)j, (double)k + 0.5D, this.tamableFox.getYRot(), this.tamableFox.getXRot());
|
||||||
|
EntityTeleportEvent event = new EntityTeleportEvent(entity, entity.getLocation(), to);
|
||||||
|
this.tamableFox.level().getCraftServer().getPluginManager().callEvent(event);
|
||||||
|
if (event.isCancelled()) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
to = event.getTo();
|
||||||
|
this.tamableFox.moveTo(to.getX(), to.getY(), to.getZ(), to.getYaw(), to.getPitch());
|
||||||
|
this.navigation.stop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canTeleportTo(BlockPos blockposition) {
|
||||||
|
BlockPathTypes pathtype = WalkNodeEvaluator.getBlockPathTypeStatic(this.level, blockposition.mutable());
|
||||||
|
if (pathtype != BlockPathTypes.WALKABLE) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
BlockState iblockdata = this.level.getBlockState(blockposition.below());
|
||||||
|
if (!this.canFly && iblockdata.getBlock() instanceof LeavesBlock) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
BlockPos blockposition1 = blockposition.subtract(this.tamableFox.blockPosition());
|
||||||
|
return this.level.noCollision(this.tamableFox, this.tamableFox.getBoundingBox().move(blockposition1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int randomIntInclusive(int i, int j) {
|
||||||
|
return this.tamableFox.getRandom().nextInt(j - i + 1) + i;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
package net.seanomik.tamablefoxes.versions.version_1_20_R3.pathfinding;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import net.minecraft.world.entity.EntitySelector;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.Mob;
|
||||||
|
import net.minecraft.world.entity.PathfinderMob;
|
||||||
|
import net.minecraft.world.entity.ai.goal.target.TargetGoal;
|
||||||
|
import net.minecraft.world.entity.ai.targeting.TargetingConditions;
|
||||||
|
import net.minecraft.world.level.GameRules;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.seanomik.tamablefoxes.versions.version_1_20_R3.EntityTamableFox;
|
||||||
|
import org.bukkit.event.entity.EntityTargetEvent.TargetReason;
|
||||||
|
|
||||||
|
public class FoxPathfinderGoalHurtByTarget extends TargetGoal {
|
||||||
|
private static final TargetingConditions HURT_BY_TARGETING = TargetingConditions.forCombat().ignoreLineOfSight().ignoreInvisibilityTesting();
|
||||||
|
private static final int ALERT_RANGE_Y = 10;
|
||||||
|
private boolean alertSameType;
|
||||||
|
private int timestamp;
|
||||||
|
private final Class<?>[] toIgnoreDamage;
|
||||||
|
private Class<?>[] toIgnoreAlert;
|
||||||
|
|
||||||
|
public FoxPathfinderGoalHurtByTarget(PathfinderMob entitycreature, Class<?>... aclass) {
|
||||||
|
super(entitycreature, true);
|
||||||
|
this.toIgnoreDamage = aclass;
|
||||||
|
this.setFlags(EnumSet.of(Flag.TARGET));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canUse() {
|
||||||
|
int i = this.mob.getLastHurtByMobTimestamp();
|
||||||
|
LivingEntity entityliving = this.mob.getLastHurtByMob();
|
||||||
|
if (i != this.timestamp && entityliving != null) {
|
||||||
|
if (entityliving.getType() == EntityType.PLAYER && this.mob.level().getGameRules().getBoolean(GameRules.RULE_UNIVERSAL_ANGER)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
Class[] aclass = this.toIgnoreDamage;
|
||||||
|
int j = aclass.length;
|
||||||
|
|
||||||
|
for(int k = 0; k < j; ++k) {
|
||||||
|
Class<?> oclass = aclass[k];
|
||||||
|
if (oclass.isAssignableFrom(entityliving.getClass())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.canAttack(entityliving, HURT_BY_TARGETING);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FoxPathfinderGoalHurtByTarget setAlertOthers(Class<?>... aclass) {
|
||||||
|
this.alertSameType = true;
|
||||||
|
this.toIgnoreAlert = aclass;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
this.mob.setTarget(this.mob.getLastHurtByMob(), TargetReason.TARGET_ATTACKED_ENTITY, true);
|
||||||
|
this.targetMob = this.mob.getTarget();
|
||||||
|
this.timestamp = this.mob.getLastHurtByMobTimestamp();
|
||||||
|
this.unseenMemoryTicks = 300;
|
||||||
|
if (this.alertSameType) {
|
||||||
|
this.alertOthers();
|
||||||
|
}
|
||||||
|
|
||||||
|
super.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void alertOthers() {
|
||||||
|
double d0 = this.getFollowDistance();
|
||||||
|
AABB axisalignedbb = AABB.unitCubeFromLowerCorner(this.mob.position()).inflate(d0, 10.0D, d0);
|
||||||
|
List<? extends Mob> list = this.mob.level().getEntitiesOfClass(this.mob.getClass(), axisalignedbb, EntitySelector.NO_SPECTATORS);
|
||||||
|
Iterator iterator = list.iterator();
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
Mob entityinsentient;
|
||||||
|
boolean flag;
|
||||||
|
do {
|
||||||
|
do {
|
||||||
|
do {
|
||||||
|
do {
|
||||||
|
do {
|
||||||
|
if (!iterator.hasNext()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entityinsentient = (Mob)iterator.next();
|
||||||
|
} while(this.mob == entityinsentient);
|
||||||
|
} while(entityinsentient.getTarget() != null);
|
||||||
|
} while(this.mob instanceof EntityTamableFox && ((EntityTamableFox)this.mob).getOwner() != ((EntityTamableFox)entityinsentient).getOwner());
|
||||||
|
} while(entityinsentient.isAlliedTo(this.mob.getLastHurtByMob()));
|
||||||
|
|
||||||
|
if (this.toIgnoreAlert == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
flag = false;
|
||||||
|
Class[] aclass = this.toIgnoreAlert;
|
||||||
|
int i = aclass.length;
|
||||||
|
|
||||||
|
for(int j = 0; j < i; ++j) {
|
||||||
|
Class<?> oclass = aclass[j];
|
||||||
|
if (entityinsentient.getClass() == oclass) {
|
||||||
|
flag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(flag);
|
||||||
|
|
||||||
|
this.alertOther(entityinsentient, this.mob.getLastHurtByMob());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void alertOther(Mob entityinsentient, LivingEntity entityliving) {
|
||||||
|
entityinsentient.setTarget(entityliving, TargetReason.TARGET_ATTACKED_NEARBY_ENTITY, true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package net.seanomik.tamablefoxes.versions.version_1_20_R3.pathfinding;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.ai.goal.target.TargetGoal;
|
||||||
|
import net.minecraft.world.entity.ai.targeting.TargetingConditions;
|
||||||
|
import net.seanomik.tamablefoxes.versions.version_1_20_R3.EntityTamableFox;
|
||||||
|
import org.bukkit.event.entity.EntityTargetEvent.TargetReason;
|
||||||
|
|
||||||
|
public class FoxPathfinderGoalOwnerHurtByTarget extends TargetGoal {
|
||||||
|
private final EntityTamableFox tameAnimal;
|
||||||
|
private LivingEntity ownerLastHurtBy;
|
||||||
|
private int timestamp;
|
||||||
|
|
||||||
|
public FoxPathfinderGoalOwnerHurtByTarget(EntityTamableFox entitytameableanimal) {
|
||||||
|
super(entitytameableanimal, false);
|
||||||
|
this.tameAnimal = entitytameableanimal;
|
||||||
|
this.setFlags(EnumSet.of(Flag.TARGET));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canUse() {
|
||||||
|
if (this.tameAnimal.isTamed() && !this.tameAnimal.isOrderedToSit() && !this.tameAnimal.isOrderedToSleep()) {
|
||||||
|
LivingEntity entityliving = this.tameAnimal.getOwner();
|
||||||
|
if (entityliving == null) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
this.ownerLastHurtBy = entityliving.getLastHurtByMob();
|
||||||
|
int i = entityliving.getLastHurtByMobTimestamp();
|
||||||
|
return i != this.timestamp && this.canAttack(this.ownerLastHurtBy, TargetingConditions.DEFAULT) && this.tameAnimal.wantsToAttack(this.ownerLastHurtBy, entityliving);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
this.mob.setTarget(this.ownerLastHurtBy, TargetReason.TARGET_ATTACKED_OWNER, true);
|
||||||
|
LivingEntity entityliving = this.tameAnimal.getOwner();
|
||||||
|
if (entityliving != null) {
|
||||||
|
this.timestamp = entityliving.getLastHurtByMobTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
tameAnimal.setDefending(false);
|
||||||
|
|
||||||
|
super.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
tameAnimal.setDefending(false);
|
||||||
|
|
||||||
|
super.stop();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package net.seanomik.tamablefoxes.versions.version_1_20_R3.pathfinding;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.ai.goal.target.TargetGoal;
|
||||||
|
import net.minecraft.world.entity.ai.targeting.TargetingConditions;
|
||||||
|
import net.seanomik.tamablefoxes.versions.version_1_20_R3.EntityTamableFox;
|
||||||
|
import org.bukkit.event.entity.EntityTargetEvent.TargetReason;
|
||||||
|
|
||||||
|
public class FoxPathfinderGoalOwnerHurtTarget extends TargetGoal {
|
||||||
|
private final EntityTamableFox tameAnimal;
|
||||||
|
private LivingEntity ownerLastHurt;
|
||||||
|
private int timestamp;
|
||||||
|
|
||||||
|
public FoxPathfinderGoalOwnerHurtTarget(EntityTamableFox entitytameableanimal) {
|
||||||
|
super(entitytameableanimal, false);
|
||||||
|
this.tameAnimal = entitytameableanimal;
|
||||||
|
this.setFlags(EnumSet.of(Flag.TARGET));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canUse() {
|
||||||
|
if (this.tameAnimal.isTamed() && !this.tameAnimal.isOrderedToSit() && !this.tameAnimal.isOrderedToSleep()) {
|
||||||
|
LivingEntity entityliving = this.tameAnimal.getOwner();
|
||||||
|
if (entityliving == null) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
this.ownerLastHurt = entityliving.getLastHurtMob();
|
||||||
|
int i = entityliving.getLastHurtMobTimestamp();
|
||||||
|
return i != this.timestamp && this.canAttack(this.ownerLastHurt, TargetingConditions.DEFAULT) && this.tameAnimal.wantsToAttack(this.ownerLastHurt, entityliving);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
this.mob.setTarget(this.ownerLastHurt, TargetReason.OWNER_ATTACKED_TARGET, true);
|
||||||
|
LivingEntity entityliving = this.tameAnimal.getOwner();
|
||||||
|
if (entityliving != null) {
|
||||||
|
this.timestamp = entityliving.getLastHurtMobTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
tameAnimal.setDefending(true);
|
||||||
|
|
||||||
|
super.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
tameAnimal.setDefending(false);
|
||||||
|
|
||||||
|
super.stop();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package net.seanomik.tamablefoxes.versions.version_1_20_R3.pathfinding;
|
||||||
|
|
||||||
|
import net.minecraft.world.entity.ai.goal.PanicGoal;
|
||||||
|
import net.seanomik.tamablefoxes.versions.version_1_20_R3.EntityTamableFox;
|
||||||
|
|
||||||
|
public class FoxPathfinderGoalPanic extends PanicGoal {
|
||||||
|
EntityTamableFox tamableFox;
|
||||||
|
|
||||||
|
public FoxPathfinderGoalPanic(EntityTamableFox tamableFox, double d0) {
|
||||||
|
super(tamableFox, d0);
|
||||||
|
this.tamableFox = tamableFox;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canUse() {
|
||||||
|
if (tamableFox.isTamed()) {
|
||||||
|
return tamableFox.getHealth() < 2.0f && super.canUse();
|
||||||
|
}
|
||||||
|
|
||||||
|
return tamableFox.isDefending() && super.canUse();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package net.seanomik.tamablefoxes.versions.version_1_20_R3.pathfinding;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.ai.goal.Goal;
|
||||||
|
import net.seanomik.tamablefoxes.util.Utils;
|
||||||
|
import net.seanomik.tamablefoxes.versions.version_1_20_R3.EntityTamableFox;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
|
public class FoxPathfinderGoalSitWhenOrdered extends Goal {
|
||||||
|
private final EntityTamableFox mob;
|
||||||
|
private boolean orderedToSit;
|
||||||
|
|
||||||
|
public FoxPathfinderGoalSitWhenOrdered(EntityTamableFox entitytameableanimal) {
|
||||||
|
this.mob = entitytameableanimal;
|
||||||
|
this.setFlags(EnumSet.of(Flag.JUMP, Flag.MOVE));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canContinueToUse() {
|
||||||
|
return this.orderedToSit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canUse() {
|
||||||
|
if (!this.mob.isTamed()) {
|
||||||
|
return this.orderedToSit && this.mob.getTarget() == null;
|
||||||
|
} else if (this.mob.isInWaterOrBubble()) {
|
||||||
|
return false;
|
||||||
|
} else if (this.mob.isFallFlying()) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
LivingEntity entityliving = this.mob.getOwner();
|
||||||
|
return entityliving == null || ((!(this.mob.distanceToSqr(entityliving) < 144.0D) || entityliving.getLastHurtByMob() == null) && this.mob.isOrderedToSit());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
// For some reason it needs to be ran later to not have the fox slide across the floor.
|
||||||
|
Bukkit.getScheduler().runTaskLater(Utils.tamableFoxesPlugin, () -> {
|
||||||
|
this.mob.getNavigation().stop();
|
||||||
|
this.mob.setSitting(true);
|
||||||
|
this.orderedToSit = true;
|
||||||
|
}, 1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
this.mob.setSitting(false);
|
||||||
|
this.orderedToSit = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOrderedToSit() { return this.orderedToSit; }
|
||||||
|
|
||||||
|
public void setOrderedToSit(boolean flag) {
|
||||||
|
this.orderedToSit = flag;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package net.seanomik.tamablefoxes.versions.version_1_20_R3.pathfinding;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.ai.goal.Goal;
|
||||||
|
import net.seanomik.tamablefoxes.versions.version_1_20_R3.EntityTamableFox;
|
||||||
|
|
||||||
|
public class FoxPathfinderGoalSleepWhenOrdered extends Goal {
|
||||||
|
private final EntityTamableFox mob;
|
||||||
|
private boolean orderedToSleep;
|
||||||
|
|
||||||
|
public FoxPathfinderGoalSleepWhenOrdered(EntityTamableFox entitytameableanimal) {
|
||||||
|
this.mob = entitytameableanimal;
|
||||||
|
this.setFlags(EnumSet.of(Flag.JUMP, Flag.MOVE));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canContinueToUse() {
|
||||||
|
return this.orderedToSleep;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canUse() {
|
||||||
|
if (!this.mob.isTamed()) {
|
||||||
|
return this.orderedToSleep && this.mob.getTarget() == null;
|
||||||
|
} else if (this.mob.isInWaterOrBubble()) {
|
||||||
|
return false;
|
||||||
|
} else if (this.mob.isFallFlying()) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
LivingEntity entityliving = this.mob.getOwner();
|
||||||
|
return entityliving == null || ((!(this.mob.distanceToSqr(entityliving) < 144.0D) || entityliving.getLastHurtByMob() == null) && this.mob.isOrderedToSleep());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
this.mob.getNavigation().stop();
|
||||||
|
this.mob.setSleeping(true);
|
||||||
|
this.orderedToSleep = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
this.mob.setSleeping(false);
|
||||||
|
this.orderedToSleep = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOrderedToSleep() { return this.orderedToSleep; }
|
||||||
|
|
||||||
|
public void setOrderedToSleep(boolean flag) {
|
||||||
|
this.orderedToSleep = flag;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
package net.seanomik.tamablefoxes.versions.version_1_20_R3.pathfinding;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.tags.BlockTags;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.ai.goal.Goal;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.level.block.BedBlock;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.seanomik.tamablefoxes.versions.version_1_20_R3.EntityTamableFox;
|
||||||
|
|
||||||
|
// From class EntityCat#b
|
||||||
|
public class FoxPathfinderGoalSleepWithOwner extends Goal {
|
||||||
|
private final EntityTamableFox fox;
|
||||||
|
private Player ownerPlayer;
|
||||||
|
private BlockPos goalPos;
|
||||||
|
private int onBedTicks;
|
||||||
|
|
||||||
|
public FoxPathfinderGoalSleepWithOwner(EntityTamableFox entityTamableFox) {
|
||||||
|
this.fox = entityTamableFox;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canUse() {
|
||||||
|
if (!this.fox.isTamed()) {
|
||||||
|
return false;
|
||||||
|
} else if (this.fox.isOrderedToSleep()) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
LivingEntity entityliving = this.fox.getOwner();
|
||||||
|
if (entityliving instanceof Player) {
|
||||||
|
this.ownerPlayer = (Player)entityliving;
|
||||||
|
if (!entityliving.isSleeping()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.fox.distanceToSqr(this.ownerPlayer) > 100.0D) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockPos blockposition = this.ownerPlayer.blockPosition();
|
||||||
|
BlockState iblockdata = this.fox.level().getBlockState(blockposition);
|
||||||
|
if (iblockdata.is(BlockTags.BEDS)) {
|
||||||
|
this.goalPos = (BlockPos)iblockdata.getOptionalValue(BedBlock.FACING).map((enumdirection) -> {
|
||||||
|
return blockposition.relative(enumdirection.getOpposite());
|
||||||
|
}).orElseGet(() -> {
|
||||||
|
return new BlockPos(blockposition);
|
||||||
|
});
|
||||||
|
return !this.spaceIsOccupied();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean spaceIsOccupied() {
|
||||||
|
List<EntityTamableFox> list = this.fox.level().getEntitiesOfClass(EntityTamableFox.class, (new AABB(this.goalPos)).inflate(2.0D));
|
||||||
|
Iterator iterator = list.iterator();
|
||||||
|
|
||||||
|
EntityTamableFox entityTamableFox;
|
||||||
|
do {
|
||||||
|
do {
|
||||||
|
if (!iterator.hasNext()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
entityTamableFox = (EntityTamableFox)iterator.next();
|
||||||
|
} while(entityTamableFox == this.fox);
|
||||||
|
} while(!entityTamableFox.isSleeping());// && !entityTamableFox.isRelaxStateOne());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canContinueToUse() {
|
||||||
|
return this.fox.isTamed() && !this.fox.isOrderedToSleep() && this.ownerPlayer != null && this.ownerPlayer.isSleeping() && this.goalPos != null && !this.spaceIsOccupied();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
if (this.goalPos != null) {
|
||||||
|
this.fox.setSitting(false);
|
||||||
|
this.fox.getNavigation().moveTo((double)this.goalPos.getX(), (double)this.goalPos.getY(), (double)this.goalPos.getZ(), 1.100000023841858D);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
this.fox.setSleeping(false);
|
||||||
|
this.onBedTicks = 0;
|
||||||
|
this.fox.getNavigation().stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tick() {
|
||||||
|
if (this.ownerPlayer != null && this.goalPos != null) {
|
||||||
|
this.fox.setSitting(false);
|
||||||
|
this.fox.getNavigation().moveTo((double)this.goalPos.getX(), (double)this.goalPos.getY(), (double)this.goalPos.getZ(), 1.100000023841858D);
|
||||||
|
if (this.fox.distanceToSqr(this.ownerPlayer) < 2.5D) {
|
||||||
|
++this.onBedTicks;
|
||||||
|
if (this.onBedTicks > 16) {
|
||||||
|
this.fox.setSleeping(true);
|
||||||
|
} else {
|
||||||
|
this.fox.lookAt(this.ownerPlayer, 45.0F, 45.0F);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.fox.setSleeping(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -93,7 +93,7 @@
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>codemc-snapshots</id>
|
<id>codemc-snapshots</id>
|
||||||
<url>https://repo.codemc.io/repository/maven-snapshots/</url>
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
@ -195,6 +195,12 @@
|
||||||
<version>${project.parent.version}</version>
|
<version>${project.parent.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.seanomik</groupId>
|
||||||
|
<artifactId>tamablefoxes_v1_20_R3</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
<!-- End of entity implementations -->
|
<!-- End of entity implementations -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bstats</groupId>
|
<groupId>org.bstats</groupId>
|
||||||
|
|
|
@ -22,6 +22,7 @@ import net.seanomik.tamablefoxes.versions.version_1_19_2_R1.NMSInterface_1_19_2_
|
||||||
import net.seanomik.tamablefoxes.versions.version_1_19_3_R1.NMSInterface_1_19_3_R1;
|
import net.seanomik.tamablefoxes.versions.version_1_19_3_R1.NMSInterface_1_19_3_R1;
|
||||||
import net.seanomik.tamablefoxes.versions.version_1_19_R3.NMSInterface_1_19_4_R1;
|
import net.seanomik.tamablefoxes.versions.version_1_19_R3.NMSInterface_1_19_4_R1;
|
||||||
import net.seanomik.tamablefoxes.versions.version_1_20_R1.NMSInterface_1_20_R1;
|
import net.seanomik.tamablefoxes.versions.version_1_20_R1.NMSInterface_1_20_R1;
|
||||||
|
import net.seanomik.tamablefoxes.versions.version_1_20_R3.NMSInterface_1_20_R3;
|
||||||
import org.bstats.bukkit.Metrics;
|
import org.bstats.bukkit.Metrics;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
|
@ -90,7 +91,10 @@ public final class TamableFoxes extends JavaPlugin implements Listener {
|
||||||
nmsInterface = new NMSInterface_1_19_4_R1();
|
nmsInterface = new NMSInterface_1_19_4_R1();
|
||||||
} else if (versionDouble == 20D || versionDouble == 20.1D) {
|
} else if (versionDouble == 20D || versionDouble == 20.1D) {
|
||||||
nmsInterface = new NMSInterface_1_20_R1();
|
nmsInterface = new NMSInterface_1_20_R1();
|
||||||
} else {
|
} else if (versionDouble == 20.3D || versionDouble == 20.4D) {
|
||||||
|
nmsInterface = new NMSInterface_1_20_R3();
|
||||||
|
|
||||||
|
} else {
|
||||||
Bukkit.getServer().getConsoleSender().sendMessage(Config.getPrefix() + ChatColor.RED + LanguageConfig.getUnsupportedMCVersionRegister());
|
Bukkit.getServer().getConsoleSender().sendMessage(Config.getPrefix() + ChatColor.RED + LanguageConfig.getUnsupportedMCVersionRegister());
|
||||||
Bukkit.getServer().getConsoleSender().sendMessage(Config.getPrefix() + ChatColor.RED + "You're trying to run MC version " + specificVersion + " which is not supported!");
|
Bukkit.getServer().getConsoleSender().sendMessage(Config.getPrefix() + ChatColor.RED + "You're trying to run MC version " + specificVersion + " which is not supported!");
|
||||||
Bukkit.getServer().getConsoleSender().sendMessage(Config.getPrefix() + "Disabling plugin...");
|
Bukkit.getServer().getConsoleSender().sendMessage(Config.getPrefix() + "Disabling plugin...");
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package net.seanomik.tamablefoxes.util;
|
package net.seanomik.tamablefoxes.util;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import sun.misc.Unsafe;
|
import sun.misc.Unsafe;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
@ -11,14 +12,18 @@ public final class FieldHelper {
|
||||||
public static void setFieldUsingUnsafe(final Field field, final Object object, final Object newValue) {
|
public static void setFieldUsingUnsafe(final Field field, final Object object, final Object newValue) {
|
||||||
try {
|
try {
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
|
System.out.println("field on accessible");
|
||||||
int fieldModifiersMask = field.getModifiers();
|
int fieldModifiersMask = field.getModifiers();
|
||||||
boolean isFinalModifierPresent = (fieldModifiersMask & Modifier.FINAL) == Modifier.FINAL;
|
boolean isFinalModifierPresent = (fieldModifiersMask & Modifier.FINAL) == Modifier.FINAL;
|
||||||
if (isFinalModifierPresent) {
|
if (isFinalModifierPresent) {
|
||||||
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
|
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
|
||||||
try {
|
try {
|
||||||
sun.misc.Unsafe unsafe = getUnsafe();
|
sun.misc.Unsafe unsafe = getUnsafe();
|
||||||
|
System.out.println("unsafe on käes");
|
||||||
long offset = unsafe.objectFieldOffset(field);
|
long offset = unsafe.objectFieldOffset(field);
|
||||||
|
System.out.println("offset mida iganes");
|
||||||
setFieldUsingUnsafe(object, field.getType(), offset, newValue, unsafe);
|
setFieldUsingUnsafe(object, field.getType(), offset, newValue, unsafe);
|
||||||
|
System.out.println("see teine setfieldusingunsafe sai hakkama");
|
||||||
return null;
|
return null;
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
throw new RuntimeException(t);
|
throw new RuntimeException(t);
|
||||||
|
|
|
@ -19,4 +19,5 @@ java -jar ./BuildTools.jar --rev 1.19.2 --remapped --compile-if-changed
|
||||||
java -jar ./BuildTools.jar --rev 1.19.3 --remapped --compile-if-changed
|
java -jar ./BuildTools.jar --rev 1.19.3 --remapped --compile-if-changed
|
||||||
java -jar ./BuildTools.jar --rev 1.19.4 --remapped --compile-if-changed
|
java -jar ./BuildTools.jar --rev 1.19.4 --remapped --compile-if-changed
|
||||||
java -jar ./BuildTools.jar --rev 1.20 --remapped --compile-if-changed
|
java -jar ./BuildTools.jar --rev 1.20 --remapped --compile-if-changed
|
||||||
java -jar ./BuildTools.jar --rev 1.21 --remapped --compile-if-changed
|
java -jar ./BuildTools.jar --rev 1.21 --remapped --compile-if-changed
|
||||||
|
java -jar ./BuildTools.jar --rev 1.20.4 --remapped --compile-if-changed
|
Loading…
Reference in New Issue