623 lines
25 KiB
Java
623 lines
25 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* org.jspecify.annotations.Nullable
|
|
*/
|
|
package net.minecraft.world.entity.animal;
|
|
|
|
import java.util.List;
|
|
import java.util.function.Predicate;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Holder;
|
|
import net.minecraft.core.component.DataComponentGetter;
|
|
import net.minecraft.core.component.DataComponentType;
|
|
import net.minecraft.core.component.DataComponents;
|
|
import net.minecraft.core.registries.Registries;
|
|
import net.minecraft.network.syncher.EntityDataAccessor;
|
|
import net.minecraft.network.syncher.EntityDataSerializers;
|
|
import net.minecraft.network.syncher.SynchedEntityData;
|
|
import net.minecraft.resources.ResourceKey;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.sounds.SoundEvent;
|
|
import net.minecraft.sounds.SoundEvents;
|
|
import net.minecraft.tags.BlockTags;
|
|
import net.minecraft.tags.ItemTags;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.util.RandomSource;
|
|
import net.minecraft.world.DifficultyInstance;
|
|
import net.minecraft.world.InteractionHand;
|
|
import net.minecraft.world.InteractionResult;
|
|
import net.minecraft.world.attribute.EnvironmentAttributes;
|
|
import net.minecraft.world.damagesource.DamageSource;
|
|
import net.minecraft.world.entity.AgeableMob;
|
|
import net.minecraft.world.entity.EntitySelector;
|
|
import net.minecraft.world.entity.EntitySpawnReason;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.entity.PathfinderMob;
|
|
import net.minecraft.world.entity.Pose;
|
|
import net.minecraft.world.entity.SpawnGroupData;
|
|
import net.minecraft.world.entity.TamableAnimal;
|
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
|
import net.minecraft.world.entity.ai.goal.AvoidEntityGoal;
|
|
import net.minecraft.world.entity.ai.goal.BreedGoal;
|
|
import net.minecraft.world.entity.ai.goal.CatLieOnBedGoal;
|
|
import net.minecraft.world.entity.ai.goal.CatSitOnBlockGoal;
|
|
import net.minecraft.world.entity.ai.goal.FloatGoal;
|
|
import net.minecraft.world.entity.ai.goal.FollowOwnerGoal;
|
|
import net.minecraft.world.entity.ai.goal.Goal;
|
|
import net.minecraft.world.entity.ai.goal.LeapAtTargetGoal;
|
|
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
|
|
import net.minecraft.world.entity.ai.goal.OcelotAttackGoal;
|
|
import net.minecraft.world.entity.ai.goal.SitWhenOrderedToGoal;
|
|
import net.minecraft.world.entity.ai.goal.TemptGoal;
|
|
import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal;
|
|
import net.minecraft.world.entity.ai.goal.target.NonTameRandomTargetGoal;
|
|
import net.minecraft.world.entity.animal.Animal;
|
|
import net.minecraft.world.entity.animal.CatVariant;
|
|
import net.minecraft.world.entity.animal.CatVariants;
|
|
import net.minecraft.world.entity.animal.Rabbit;
|
|
import net.minecraft.world.entity.animal.Turtle;
|
|
import net.minecraft.world.entity.item.ItemEntity;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.entity.variant.SpawnContext;
|
|
import net.minecraft.world.entity.variant.VariantUtils;
|
|
import net.minecraft.world.food.FoodProperties;
|
|
import net.minecraft.world.item.DyeColor;
|
|
import net.minecraft.world.item.DyeItem;
|
|
import net.minecraft.world.item.Item;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.ServerLevelAccessor;
|
|
import net.minecraft.world.level.block.BedBlock;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.storage.ValueInput;
|
|
import net.minecraft.world.level.storage.ValueOutput;
|
|
import net.minecraft.world.level.storage.loot.BuiltInLootTables;
|
|
import net.minecraft.world.phys.AABB;
|
|
import org.jspecify.annotations.Nullable;
|
|
|
|
public class Cat
|
|
extends TamableAnimal {
|
|
public static final double TEMPT_SPEED_MOD = 0.6;
|
|
public static final double WALK_SPEED_MOD = 0.8;
|
|
public static final double SPRINT_SPEED_MOD = 1.33;
|
|
private static final EntityDataAccessor<Holder<CatVariant>> DATA_VARIANT_ID = SynchedEntityData.defineId(Cat.class, EntityDataSerializers.CAT_VARIANT);
|
|
private static final EntityDataAccessor<Boolean> IS_LYING = SynchedEntityData.defineId(Cat.class, EntityDataSerializers.BOOLEAN);
|
|
private static final EntityDataAccessor<Boolean> RELAX_STATE_ONE = SynchedEntityData.defineId(Cat.class, EntityDataSerializers.BOOLEAN);
|
|
private static final EntityDataAccessor<Integer> DATA_COLLAR_COLOR = SynchedEntityData.defineId(Cat.class, EntityDataSerializers.INT);
|
|
private static final ResourceKey<CatVariant> DEFAULT_VARIANT = CatVariants.BLACK;
|
|
private static final DyeColor DEFAULT_COLLAR_COLOR = DyeColor.RED;
|
|
private @Nullable CatAvoidEntityGoal<Player> avoidPlayersGoal;
|
|
private @Nullable TemptGoal temptGoal;
|
|
private float lieDownAmount;
|
|
private float lieDownAmountO;
|
|
private float lieDownAmountTail;
|
|
private float lieDownAmountOTail;
|
|
private boolean isLyingOnTopOfSleepingPlayer;
|
|
private float relaxStateOneAmount;
|
|
private float relaxStateOneAmountO;
|
|
|
|
public Cat(EntityType<? extends Cat> type, Level level) {
|
|
super((EntityType<? extends TamableAnimal>)type, level);
|
|
this.reassessTameGoals();
|
|
}
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.temptGoal = new CatTemptGoal(this, 0.6, i -> i.is(ItemTags.CAT_FOOD), true);
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
this.goalSelector.addGoal(1, new TamableAnimal.TamableAnimalPanicGoal(1.5));
|
|
this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this));
|
|
this.goalSelector.addGoal(3, new CatRelaxOnOwnerGoal(this));
|
|
this.goalSelector.addGoal(4, this.temptGoal);
|
|
this.goalSelector.addGoal(5, new CatLieOnBedGoal(this, 1.1, 8));
|
|
this.goalSelector.addGoal(6, new FollowOwnerGoal(this, 1.0, 10.0f, 5.0f));
|
|
this.goalSelector.addGoal(7, new CatSitOnBlockGoal(this, 0.8));
|
|
this.goalSelector.addGoal(8, new LeapAtTargetGoal(this, 0.3f));
|
|
this.goalSelector.addGoal(9, new OcelotAttackGoal(this));
|
|
this.goalSelector.addGoal(10, new BreedGoal(this, 0.8));
|
|
this.goalSelector.addGoal(11, new WaterAvoidingRandomStrollGoal((PathfinderMob)this, 0.8, 1.0000001E-5f));
|
|
this.goalSelector.addGoal(12, new LookAtPlayerGoal(this, Player.class, 10.0f));
|
|
this.targetSelector.addGoal(1, new NonTameRandomTargetGoal<Rabbit>(this, Rabbit.class, false, null));
|
|
this.targetSelector.addGoal(1, new NonTameRandomTargetGoal<Turtle>(this, Turtle.class, false, Turtle.BABY_ON_LAND_SELECTOR));
|
|
}
|
|
|
|
public Holder<CatVariant> getVariant() {
|
|
return this.entityData.get(DATA_VARIANT_ID);
|
|
}
|
|
|
|
private void setVariant(Holder<CatVariant> variant) {
|
|
this.entityData.set(DATA_VARIANT_ID, variant);
|
|
}
|
|
|
|
@Override
|
|
public <T> @Nullable T get(DataComponentType<? extends T> type) {
|
|
if (type == DataComponents.CAT_VARIANT) {
|
|
return Cat.castComponentValue(type, this.getVariant());
|
|
}
|
|
if (type == DataComponents.CAT_COLLAR) {
|
|
return Cat.castComponentValue(type, this.getCollarColor());
|
|
}
|
|
return super.get(type);
|
|
}
|
|
|
|
@Override
|
|
protected void applyImplicitComponents(DataComponentGetter components) {
|
|
this.applyImplicitComponentIfPresent(components, DataComponents.CAT_VARIANT);
|
|
this.applyImplicitComponentIfPresent(components, DataComponents.CAT_COLLAR);
|
|
super.applyImplicitComponents(components);
|
|
}
|
|
|
|
@Override
|
|
protected <T> boolean applyImplicitComponent(DataComponentType<T> type, T value) {
|
|
if (type == DataComponents.CAT_VARIANT) {
|
|
this.setVariant(Cat.castComponentValue(DataComponents.CAT_VARIANT, value));
|
|
return true;
|
|
}
|
|
if (type == DataComponents.CAT_COLLAR) {
|
|
this.setCollarColor(Cat.castComponentValue(DataComponents.CAT_COLLAR, value));
|
|
return true;
|
|
}
|
|
return super.applyImplicitComponent(type, value);
|
|
}
|
|
|
|
public void setLying(boolean value) {
|
|
this.entityData.set(IS_LYING, value);
|
|
}
|
|
|
|
public boolean isLying() {
|
|
return this.entityData.get(IS_LYING);
|
|
}
|
|
|
|
private void setRelaxStateOne(boolean value) {
|
|
this.entityData.set(RELAX_STATE_ONE, value);
|
|
}
|
|
|
|
private boolean isRelaxStateOne() {
|
|
return this.entityData.get(RELAX_STATE_ONE);
|
|
}
|
|
|
|
public DyeColor getCollarColor() {
|
|
return DyeColor.byId(this.entityData.get(DATA_COLLAR_COLOR));
|
|
}
|
|
|
|
private void setCollarColor(DyeColor color) {
|
|
this.entityData.set(DATA_COLLAR_COLOR, color.getId());
|
|
}
|
|
|
|
@Override
|
|
protected void defineSynchedData(SynchedEntityData.Builder entityData) {
|
|
super.defineSynchedData(entityData);
|
|
entityData.define(DATA_VARIANT_ID, VariantUtils.getDefaultOrAny(this.registryAccess(), DEFAULT_VARIANT));
|
|
entityData.define(IS_LYING, false);
|
|
entityData.define(RELAX_STATE_ONE, false);
|
|
entityData.define(DATA_COLLAR_COLOR, DEFAULT_COLLAR_COLOR.getId());
|
|
}
|
|
|
|
@Override
|
|
protected void addAdditionalSaveData(ValueOutput output) {
|
|
super.addAdditionalSaveData(output);
|
|
VariantUtils.writeVariant(output, this.getVariant());
|
|
output.store("CollarColor", DyeColor.LEGACY_ID_CODEC, this.getCollarColor());
|
|
}
|
|
|
|
@Override
|
|
protected void readAdditionalSaveData(ValueInput input) {
|
|
super.readAdditionalSaveData(input);
|
|
VariantUtils.readVariant(input, Registries.CAT_VARIANT).ifPresent(this::setVariant);
|
|
this.setCollarColor(input.read("CollarColor", DyeColor.LEGACY_ID_CODEC).orElse(DEFAULT_COLLAR_COLOR));
|
|
}
|
|
|
|
@Override
|
|
public void customServerAiStep(ServerLevel level) {
|
|
if (this.getMoveControl().hasWanted()) {
|
|
double speed = this.getMoveControl().getSpeedModifier();
|
|
if (speed == 0.6) {
|
|
this.setPose(Pose.CROUCHING);
|
|
this.setSprinting(false);
|
|
} else if (speed == 1.33) {
|
|
this.setPose(Pose.STANDING);
|
|
this.setSprinting(true);
|
|
} else {
|
|
this.setPose(Pose.STANDING);
|
|
this.setSprinting(false);
|
|
}
|
|
} else {
|
|
this.setPose(Pose.STANDING);
|
|
this.setSprinting(false);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected @Nullable SoundEvent getAmbientSound() {
|
|
if (this.isTame()) {
|
|
if (this.isInLove()) {
|
|
return SoundEvents.CAT_PURR;
|
|
}
|
|
if (this.random.nextInt(4) == 0) {
|
|
return SoundEvents.CAT_PURREOW;
|
|
}
|
|
return SoundEvents.CAT_AMBIENT;
|
|
}
|
|
return SoundEvents.CAT_STRAY_AMBIENT;
|
|
}
|
|
|
|
@Override
|
|
public int getAmbientSoundInterval() {
|
|
return 120;
|
|
}
|
|
|
|
public void hiss() {
|
|
this.makeSound(SoundEvents.CAT_HISS);
|
|
}
|
|
|
|
@Override
|
|
protected SoundEvent getHurtSound(DamageSource source) {
|
|
return SoundEvents.CAT_HURT;
|
|
}
|
|
|
|
@Override
|
|
protected SoundEvent getDeathSound() {
|
|
return SoundEvents.CAT_DEATH;
|
|
}
|
|
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
return Animal.createAnimalAttributes().add(Attributes.MAX_HEALTH, 10.0).add(Attributes.MOVEMENT_SPEED, 0.3f).add(Attributes.ATTACK_DAMAGE, 3.0);
|
|
}
|
|
|
|
@Override
|
|
protected void playEatingSound() {
|
|
this.playSound(SoundEvents.CAT_EAT, 1.0f, 1.0f);
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
super.tick();
|
|
if (this.temptGoal != null && this.temptGoal.isRunning() && !this.isTame() && this.tickCount % 100 == 0) {
|
|
this.playSound(SoundEvents.CAT_BEG_FOR_FOOD, 1.0f, 1.0f);
|
|
}
|
|
this.handleLieDown();
|
|
}
|
|
|
|
private void handleLieDown() {
|
|
if ((this.isLying() || this.isRelaxStateOne()) && this.tickCount % 5 == 0) {
|
|
this.playSound(SoundEvents.CAT_PURR, 0.6f + 0.4f * (this.random.nextFloat() - this.random.nextFloat()), 1.0f);
|
|
}
|
|
this.updateLieDownAmount();
|
|
this.updateRelaxStateOneAmount();
|
|
this.isLyingOnTopOfSleepingPlayer = false;
|
|
if (this.isLying()) {
|
|
BlockPos catPos = this.blockPosition();
|
|
List<Player> players = this.level().getEntitiesOfClass(Player.class, new AABB(catPos).inflate(2.0, 2.0, 2.0));
|
|
for (Player player : players) {
|
|
if (!player.isSleeping()) continue;
|
|
this.isLyingOnTopOfSleepingPlayer = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public boolean isLyingOnTopOfSleepingPlayer() {
|
|
return this.isLyingOnTopOfSleepingPlayer;
|
|
}
|
|
|
|
private void updateLieDownAmount() {
|
|
this.lieDownAmountO = this.lieDownAmount;
|
|
this.lieDownAmountOTail = this.lieDownAmountTail;
|
|
if (this.isLying()) {
|
|
this.lieDownAmount = Math.min(1.0f, this.lieDownAmount + 0.15f);
|
|
this.lieDownAmountTail = Math.min(1.0f, this.lieDownAmountTail + 0.08f);
|
|
} else {
|
|
this.lieDownAmount = Math.max(0.0f, this.lieDownAmount - 0.22f);
|
|
this.lieDownAmountTail = Math.max(0.0f, this.lieDownAmountTail - 0.13f);
|
|
}
|
|
}
|
|
|
|
private void updateRelaxStateOneAmount() {
|
|
this.relaxStateOneAmountO = this.relaxStateOneAmount;
|
|
this.relaxStateOneAmount = this.isRelaxStateOne() ? Math.min(1.0f, this.relaxStateOneAmount + 0.1f) : Math.max(0.0f, this.relaxStateOneAmount - 0.13f);
|
|
}
|
|
|
|
public float getLieDownAmount(float a) {
|
|
return Mth.lerp(a, this.lieDownAmountO, this.lieDownAmount);
|
|
}
|
|
|
|
public float getLieDownAmountTail(float a) {
|
|
return Mth.lerp(a, this.lieDownAmountOTail, this.lieDownAmountTail);
|
|
}
|
|
|
|
public float getRelaxStateOneAmount(float a) {
|
|
return Mth.lerp(a, this.relaxStateOneAmountO, this.relaxStateOneAmount);
|
|
}
|
|
|
|
@Override
|
|
public @Nullable Cat getBreedOffspring(ServerLevel level, AgeableMob partner) {
|
|
Cat baby = EntityType.CAT.create(level, EntitySpawnReason.BREEDING);
|
|
if (baby != null && partner instanceof Cat) {
|
|
Cat partnerCat = (Cat)partner;
|
|
if (this.random.nextBoolean()) {
|
|
baby.setVariant(this.getVariant());
|
|
} else {
|
|
baby.setVariant(partnerCat.getVariant());
|
|
}
|
|
if (this.isTame()) {
|
|
baby.setOwnerReference(this.getOwnerReference());
|
|
baby.setTame(true, true);
|
|
DyeColor parent1CollarColor = this.getCollarColor();
|
|
DyeColor parent2CollarColor = partnerCat.getCollarColor();
|
|
baby.setCollarColor(DyeColor.getMixedColor(level, parent1CollarColor, parent2CollarColor));
|
|
}
|
|
}
|
|
return baby;
|
|
}
|
|
|
|
@Override
|
|
public boolean canMate(Animal partner) {
|
|
if (!this.isTame()) {
|
|
return false;
|
|
}
|
|
if (!(partner instanceof Cat)) {
|
|
return false;
|
|
}
|
|
Cat cat = (Cat)partner;
|
|
return cat.isTame() && super.canMate(partner);
|
|
}
|
|
|
|
@Override
|
|
public @Nullable SpawnGroupData finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData groupData) {
|
|
groupData = super.finalizeSpawn(level, difficulty, spawnReason, groupData);
|
|
VariantUtils.selectVariantToSpawn(SpawnContext.create(level, this.blockPosition()), Registries.CAT_VARIANT).ifPresent(this::setVariant);
|
|
return groupData;
|
|
}
|
|
|
|
@Override
|
|
public InteractionResult mobInteract(Player player, InteractionHand hand) {
|
|
InteractionResult interact;
|
|
ItemStack itemStack = player.getItemInHand(hand);
|
|
Item item = itemStack.getItem();
|
|
if (this.isTame()) {
|
|
if (this.isOwnedBy(player)) {
|
|
InteractionResult parentInteraction;
|
|
if (item instanceof DyeItem) {
|
|
DyeItem dyeItem = (DyeItem)item;
|
|
DyeColor color = dyeItem.getDyeColor();
|
|
if (color != this.getCollarColor()) {
|
|
if (!this.level().isClientSide()) {
|
|
this.setCollarColor(color);
|
|
itemStack.consume(1, player);
|
|
this.setPersistenceRequired();
|
|
}
|
|
return InteractionResult.SUCCESS;
|
|
}
|
|
} else if (this.isFood(itemStack) && this.getHealth() < this.getMaxHealth()) {
|
|
if (!this.level().isClientSide()) {
|
|
this.usePlayerItem(player, hand, itemStack);
|
|
FoodProperties foodProperties = itemStack.get(DataComponents.FOOD);
|
|
this.heal(foodProperties != null ? (float)foodProperties.nutrition() : 1.0f);
|
|
this.playEatingSound();
|
|
}
|
|
return InteractionResult.SUCCESS;
|
|
}
|
|
if (!(parentInteraction = super.mobInteract(player, hand)).consumesAction()) {
|
|
this.setOrderedToSit(!this.isOrderedToSit());
|
|
return InteractionResult.SUCCESS;
|
|
}
|
|
return parentInteraction;
|
|
}
|
|
} else if (this.isFood(itemStack)) {
|
|
if (!this.level().isClientSide()) {
|
|
this.usePlayerItem(player, hand, itemStack);
|
|
this.tryToTame(player);
|
|
this.setPersistenceRequired();
|
|
this.playEatingSound();
|
|
}
|
|
return InteractionResult.SUCCESS;
|
|
}
|
|
if ((interact = super.mobInteract(player, hand)).consumesAction()) {
|
|
this.setPersistenceRequired();
|
|
}
|
|
return interact;
|
|
}
|
|
|
|
@Override
|
|
public boolean isFood(ItemStack itemStack) {
|
|
return itemStack.is(ItemTags.CAT_FOOD);
|
|
}
|
|
|
|
@Override
|
|
public boolean removeWhenFarAway(double distSqr) {
|
|
return !this.isTame() && this.tickCount > 2400;
|
|
}
|
|
|
|
@Override
|
|
public void setTame(boolean isTame, boolean includeSideEffects) {
|
|
super.setTame(isTame, includeSideEffects);
|
|
this.reassessTameGoals();
|
|
}
|
|
|
|
protected void reassessTameGoals() {
|
|
if (this.avoidPlayersGoal == null) {
|
|
this.avoidPlayersGoal = new CatAvoidEntityGoal<Player>(this, Player.class, 16.0f, 0.8, 1.33);
|
|
}
|
|
this.goalSelector.removeGoal(this.avoidPlayersGoal);
|
|
if (!this.isTame()) {
|
|
this.goalSelector.addGoal(4, this.avoidPlayersGoal);
|
|
}
|
|
}
|
|
|
|
private void tryToTame(Player player) {
|
|
if (this.random.nextInt(3) == 0) {
|
|
this.tame(player);
|
|
this.setOrderedToSit(true);
|
|
this.level().broadcastEntityEvent(this, (byte)7);
|
|
} else {
|
|
this.level().broadcastEntityEvent(this, (byte)6);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isSteppingCarefully() {
|
|
return this.isCrouching() || super.isSteppingCarefully();
|
|
}
|
|
|
|
private static class CatTemptGoal
|
|
extends TemptGoal {
|
|
private @Nullable Player selectedPlayer;
|
|
private final Cat cat;
|
|
|
|
public CatTemptGoal(Cat mob, double speedModifier, Predicate<ItemStack> items, boolean canScare) {
|
|
super(mob, speedModifier, items, canScare);
|
|
this.cat = mob;
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
super.tick();
|
|
if (this.selectedPlayer == null && this.mob.getRandom().nextInt(this.adjustedTickDelay(600)) == 0) {
|
|
this.selectedPlayer = this.player;
|
|
} else if (this.mob.getRandom().nextInt(this.adjustedTickDelay(500)) == 0) {
|
|
this.selectedPlayer = null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected boolean canScare() {
|
|
if (this.selectedPlayer != null && this.selectedPlayer.equals(this.player)) {
|
|
return false;
|
|
}
|
|
return super.canScare();
|
|
}
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
return super.canUse() && !this.cat.isTame();
|
|
}
|
|
}
|
|
|
|
private static class CatRelaxOnOwnerGoal
|
|
extends Goal {
|
|
private final Cat cat;
|
|
private @Nullable Player ownerPlayer;
|
|
private @Nullable BlockPos goalPos;
|
|
private int onBedTicks;
|
|
|
|
public CatRelaxOnOwnerGoal(Cat cat) {
|
|
this.cat = cat;
|
|
}
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
if (!this.cat.isTame()) {
|
|
return false;
|
|
}
|
|
if (this.cat.isOrderedToSit()) {
|
|
return false;
|
|
}
|
|
LivingEntity owner = this.cat.getOwner();
|
|
if (owner instanceof Player) {
|
|
Player playerOwner;
|
|
this.ownerPlayer = playerOwner = (Player)owner;
|
|
if (!owner.isSleeping()) {
|
|
return false;
|
|
}
|
|
if (this.cat.distanceToSqr(this.ownerPlayer) > 100.0) {
|
|
return false;
|
|
}
|
|
BlockPos ownerPos = this.ownerPlayer.blockPosition();
|
|
BlockState ownerPosState = this.cat.level().getBlockState(ownerPos);
|
|
if (ownerPosState.is(BlockTags.BEDS)) {
|
|
this.goalPos = ownerPosState.getOptionalValue(BedBlock.FACING).map(bedDir -> ownerPos.relative(bedDir.getOpposite())).orElseGet(() -> new BlockPos(ownerPos));
|
|
return !this.spaceIsOccupied();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private boolean spaceIsOccupied() {
|
|
List<Cat> cats = this.cat.level().getEntitiesOfClass(Cat.class, new AABB(this.goalPos).inflate(2.0));
|
|
for (Cat otherCat : cats) {
|
|
if (otherCat == this.cat || !otherCat.isLying() && !otherCat.isRelaxStateOne()) continue;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean canContinueToUse() {
|
|
return this.cat.isTame() && !this.cat.isOrderedToSit() && this.ownerPlayer != null && this.ownerPlayer.isSleeping() && this.goalPos != null && !this.spaceIsOccupied();
|
|
}
|
|
|
|
@Override
|
|
public void start() {
|
|
if (this.goalPos != null) {
|
|
this.cat.setInSittingPose(false);
|
|
this.cat.getNavigation().moveTo(this.goalPos.getX(), this.goalPos.getY(), this.goalPos.getZ(), 1.1f);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void stop() {
|
|
this.cat.setLying(false);
|
|
if (this.ownerPlayer.getSleepTimer() >= 100 && this.cat.level().getRandom().nextFloat() < this.cat.level().environmentAttributes().getValue(EnvironmentAttributes.CAT_WAKING_UP_GIFT_CHANCE, this.cat.position()).floatValue()) {
|
|
this.giveMorningGift();
|
|
}
|
|
this.onBedTicks = 0;
|
|
this.cat.setRelaxStateOne(false);
|
|
this.cat.getNavigation().stop();
|
|
}
|
|
|
|
private void giveMorningGift() {
|
|
RandomSource random = this.cat.getRandom();
|
|
BlockPos.MutableBlockPos catPos = new BlockPos.MutableBlockPos();
|
|
catPos.set(this.cat.isLeashed() ? this.cat.getLeashHolder().blockPosition() : this.cat.blockPosition());
|
|
this.cat.randomTeleport(catPos.getX() + random.nextInt(11) - 5, catPos.getY() + random.nextInt(5) - 2, catPos.getZ() + random.nextInt(11) - 5, false);
|
|
catPos.set(this.cat.blockPosition());
|
|
this.cat.dropFromGiftLootTable(CatRelaxOnOwnerGoal.getServerLevel(this.cat), BuiltInLootTables.CAT_MORNING_GIFT, (level, itemStack) -> level.addFreshEntity(new ItemEntity((Level)level, (double)catPos.getX() - (double)Mth.sin(this.cat.yBodyRot * ((float)Math.PI / 180)), catPos.getY(), (double)catPos.getZ() + (double)Mth.cos(this.cat.yBodyRot * ((float)Math.PI / 180)), (ItemStack)itemStack)));
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
if (this.ownerPlayer != null && this.goalPos != null) {
|
|
this.cat.setInSittingPose(false);
|
|
this.cat.getNavigation().moveTo(this.goalPos.getX(), this.goalPos.getY(), this.goalPos.getZ(), 1.1f);
|
|
if (this.cat.distanceToSqr(this.ownerPlayer) < 2.5) {
|
|
++this.onBedTicks;
|
|
if (this.onBedTicks > this.adjustedTickDelay(16)) {
|
|
this.cat.setLying(true);
|
|
this.cat.setRelaxStateOne(false);
|
|
} else {
|
|
this.cat.lookAt(this.ownerPlayer, 45.0f, 45.0f);
|
|
this.cat.setRelaxStateOne(true);
|
|
}
|
|
} else {
|
|
this.cat.setLying(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static class CatAvoidEntityGoal<T extends LivingEntity>
|
|
extends AvoidEntityGoal<T> {
|
|
private final Cat cat;
|
|
|
|
public CatAvoidEntityGoal(Cat cat, Class<T> avoidClass, float maxDist, double walkSpeedModifier, double sprintSpeedModifier) {
|
|
super(cat, avoidClass, maxDist, walkSpeedModifier, sprintSpeedModifier, EntitySelector.NO_CREATIVE_OR_SPECTATOR);
|
|
this.cat = cat;
|
|
}
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
return !this.cat.isTame() && super.canUse();
|
|
}
|
|
|
|
@Override
|
|
public boolean canContinueToUse() {
|
|
return !this.cat.isTame() && super.canContinueToUse();
|
|
}
|
|
}
|
|
}
|
|
|