I created a temp command with this syntax:
/isfood <entity to check> <player to check the item in main hand or off hand>
It outputs either string respiration of an ItemStack (<count of items> <item name>) that player holds in his hand if it's a food item for specified entity, or "none" if specified player isn't holding an item which is a food item for the specified entity.
Command's code if you didn't understand:
event.getDispatcher().register(Commands.literal("isfood").then(Commands.argument("animal", EntityArgument.entity())
.then(Commands.argument("player",
EntityArgument.player()).executes(context -> {
Entity entity = EntityArgument.getEntity(context, "animal");
Player player = EntityArgument.getPlayer(context, "player");
if (entity instanceof Animal mob) {
if (mob.isFood(player.getItemInHand(InteractionHand.MAIN_HAND)))
context.getSource().sendSuccess(() ->
Component.literal(String.valueOf(
player.getItemInHand(InteractionHand.MAIN_HAND))),
true);
else if (mob.isFood(player.getItemInHand(InteractionHand.OFF_HAND)))
context.getSource().sendSuccess(() ->
Component.literal(String.valueOf(
player.getItemInHand(InteractionHand.OFF_HAND)
)), true);
else
context.getSource().sendSuccess(() ->
Component.literal("none"), true);
}
return 1;
}))));
So, I tested it on my entity, and command output was:
0 air
(also want to say that it happens only if I play Minecraft for a long time)
Anyways, the entity code:
package mymod.entities.chicken;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.*;
import net.minecraft.world.entity.animal.Animal;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.pathfinder.BlockPathTypes;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import mymod._Entities;
public abstract class AbstractChicken extends Animal implements IAbstractChicken {
protected static final Ingredient FOOD_ITEMS
= Ingredient.of(Items.WHEAT_SEEDS,
Items.MELON_SEEDS, Items.PUMPKIN_SEEDS,
Items.BEETROOT_SEEDS, Items.TORCHFLOWER_SEEDS, Items.PITCHER_POD);
public float flap;
public float flapSpeed;
public float oFlapSpeed;
public float oFlap;
public float flapping = 1.0F;
protected float nextFlap = 1.0F;
protected AbstractChicken(EntityType<? extends AbstractChicken> entityType, Level world) {
super(entityType, world);
setPathfindingMalus(BlockPathTypes.WATER, 0.0F);
}
protected SoundEvent getAmbientSound() {
return SoundEvents.CHICKEN_AMBIENT;
}
protected SoundEvent getHurtSound(@NotNull DamageSource damageSource) {
return SoundEvents.CHICKEN_HURT;
}
protected SoundEvent getDeathSound() {
return SoundEvents.CHICKEN_DEATH;
}
@Override
protected void playStepSound(@NotNull BlockPos atPos,
@NotNull BlockState atState) {
playSound(SoundEvents.CHICKEN_STEP, .15f, 1);
}
@Nullable
@Override
public AgeableMob getBreedOffspring(@NotNull ServerLevel level,
@NotNull AgeableMob mobBredWith) {
return null;
}
@Override
protected abstract void registerGoals();
@Override
public abstract boolean isBaby();
@Override
public abstract boolean canMate(@NotNull Animal other);
@Override
public final void aiStep() {
super.aiStep();
this.oFlap = flap;
this.oFlapSpeed = flapSpeed;
this.flapSpeed += (this.onGround() ? -1.0F : 4.0F) * 0.3F;
this.flapSpeed = Mth.clamp(this.flapSpeed, 0.0F, 1.0F);
if (!this.onGround() && this.flapping < 1.0F) {
this.flapping = 1.0F;
}
this.flapping *= 0.9F;
Vec3 vec3 = this.getDeltaMovement();
if (!this.onGround() && vec3.y < 0.0D) {
this.setDeltaMovement(vec3.multiply(1.0D, 0.6D, 1.0D));
}
this.flap += this.flapping * 2.0F;
doAIStep();
}
protected abstract void doAIStep();
@Override
protected float getStandingEyeHeight(@NotNull Pose pose, @NotNull EntityDimensions dimensions) {
return this.isBaby() ? dimensions.height * 0.85F : dimensions.height * 0.92F;
}
@Override
public abstract void setBaby(boolean baby);
@Override
@Nullable
public <T extends Mob> T convertTo(@NotNull EntityType<T> toType, boolean copyInventory) {
if (isRemoved()) {
return null;
} else {
T t = toType.create(level());
if (t == null) {
return null;
} else {
t.copyPosition(this);
t.setNoAi(isNoAi());
if (!(toType == EntityType.CHICKEN
|| toType == _Entities.ROOSTER.get()
|| toType == _Entities.CHICK.get())) t.setBaby(isBaby());
if (hasCustomName()) {
t.setCustomName(getCustomName());
t.setCustomNameVisible(isCustomNameVisible());
}
if (isPersistenceRequired()) {
t.setPersistenceRequired();
}
t.setInvulnerable(isInvulnerable());
if (copyInventory) {
t.setCanPickUpLoot(canPickUpLoot());
for (EquipmentSlot equipmentslot : EquipmentSlot.values()) {
ItemStack itemstack = self().getItemBySlot(equipmentslot);
if (!itemstack.isEmpty()) {
t.setItemSlot(equipmentslot, itemstack.copyAndClear());
t.setDropChance(equipmentslot, getEquipmentDropChance(equipmentslot));
}
}
}
level().addFreshEntity(t);
if (isPassenger()) {
Entity entity = getVehicle();
stopRiding();
t.startRiding(entity, true);
}
discard();
return t;
}
}
}
public abstract boolean isBoy();
@Override
public boolean isFood(@NotNull ItemStack item) {
return FOOD_ITEMS.test(item);
}
@Override
protected boolean isFlapping() {
return flyDist > nextFlap;
}
@Override
protected void onFlap() {
nextFlap = flyDist + flapSpeed / 2f;
}
public static boolean canSpawn(EntityType<? extends AbstractChicken> entityType,
LevelAccessor level,
MobSpawnType spawnType,
BlockPos pos,
RandomSource random) {
return entityType != _Entities.CHICK.get()
&& Animal.checkAnimalSpawnRules(entityType, level, spawnType, pos, random);
}
}
package mymod.entities.chicken;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.Immutable;
import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
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.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.stats.Stats;
import net.minecraft.util.RandomSource;
import net.minecraft.util.TimeUtil;
import net.minecraft.util.valueproviders.UniformInt;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.*;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.goal.*;
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
import net.minecraft.world.entity.ai.goal.target.ResetUniversalAngerTargetGoal;
import net.minecraft.world.entity.animal.*;
import net.minecraft.world.entity.monster.Zombie;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import oshi.util.tuples.Pair;
import ru.fokinatorr.chickenmod._Entities;
import ru.fokinatorr.chickenmod._EntityDataSerializers;
import mymod._Items;
import mymod._Sounds;
import mymod.ai.ZombieAttackChickenEggGoal;
import mymod.entities.BredWithMobStorable;
import mymod.util.delayedtask.DelayedTask;
import mymod.util.delayedtask.DelayedTasksHolder;
import java.util.EnumSet;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Supplier;
public class Rooster extends AbstractChicken implements NeutralMob, BredWithMobStorable,
DelayedTasksHolder<Rooster> {
private static final UniformInt PERSISTENT_ANGER_TIME
= TimeUtil.rangeOfSeconds(60, 1800);
private int remainingPersistentAngerTime;
private UUID persistentAngerTarget;
private boolean crowing;
private int crowTicks;
private int diggingAnimTicks;
private int featherDropCoolDown;
private static final EntityDataAccessor<FourFeathers> DATA_FEATHERS
= SynchedEntityData.defineId(Rooster.class, _EntityDataSerializers.ROOSTER_FEATHERS.get());
private static final EntityDataAccessor<Optional<UUID>> DATA_MOB_BRED_WITH
= SynchedEntityData.defineId(Rooster.class, EntityDataSerializers.OPTIONAL_UUID);
public Rooster(EntityType<? extends Rooster> entityType, Level world) {
super(entityType, world);
}
@Override
public int getRemainingPersistentAngerTime() {
return remainingPersistentAngerTime;
}
@Override
public void setRemainingPersistentAngerTime(int remainingPersistentAngerTime) {
this.remainingPersistentAngerTime = remainingPersistentAngerTime;
}
@Nullable
@Override
public UUID getPersistentAngerTarget() {
return persistentAngerTarget;
}
@Override
public void setPersistentAngerTarget(@Nullable UUID persistentAngerTarget) {
this.persistentAngerTarget = persistentAngerTarget;
}
@Override
public void startPersistentAngerTimer() {
this.remainingPersistentAngerTime = PERSISTENT_ANGER_TIME.sample(random);
}
@Override
protected void defineSynchedData() {
super.defineSynchedData();
entityData.define(DATA_FEATHERS, FourFeathers.NONE);
entityData.define(DATA_MOB_BRED_WITH, Optional.empty());
}
public FourFeathers getFeatherData() {
return entityData.get(DATA_FEATHERS);
}
public void setFeatherData(FourFeathers featherData) {
entityData.set(DATA_FEATHERS, featherData);
}
@Override
public void addAdditionalSaveData(@NotNull CompoundTag nbt) {
super.addAdditionalSaveData(nbt);
addPersistentAngerSaveData(nbt);
nbt.put("FeatherData", getFeatherData().writeTag());
nbt.putBoolean("isCrowing", crowing);
}
@Override
public void readAdditionalSaveData(@NotNull CompoundTag nbt) {
super.readAdditionalSaveData(nbt);
readPersistentAngerSaveData(level(), nbt);
if (nbt.contains("FeatherData", Tag.TAG_COMPOUND))
setFeatherData(FourFeathers.readTag(nbt.getCompound("FeatherData")));
else
setFeatherData(FourFeathers.NONE);
crowing = nbt.contains("isCrowing", Tag.TAG_BYTE) && nbt.getBoolean("isCrowing");
}
@Override
protected void registerGoals() {
goalSelector.addGoal(0, new FloatGoal(this));
goalSelector.addGoal(1, new RoosterMeleeAttackGoal());
goalSelector.addGoal(1, new RoosterRandomTryFindFoodGoal());
goalSelector.addGoal(2, new RoosterBreedGoal());
goalSelector.addGoal(3, new TemptGoal(this, 1, FOOD_ITEMS, false));
goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1));
goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6));
goalSelector.addGoal(7, new RandomLookAroundGoal(this));
targetSelector.addGoal(4, new RoosterRandomAttackNearbyRoosterGoal());
targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, LivingEntity.class,
true, entity -> entity instanceof Fox
|| (entity instanceof Ocelot ocelot && !ocelot.isBaby())
|| (entity instanceof Zombie zombie && zombie.goalSelector
.getRunningGoals().anyMatch(wrappedGoal -> wrappedGoal.getGoal() instanceof ZombieAttackChickenEggGoal))
|| isAngryAt(entity)));
targetSelector.addGoal(2, new HurtByTargetGoal(this));
targetSelector.addGoal(3, new ResetUniversalAngerTargetGoal<>(this, false));
}
@Override
public void setMobBredWith(@Nullable UUID uuid) {
entityData.set(DATA_MOB_BRED_WITH, Optional.ofNullable(uuid));
}
@Override
@Nullable
public UUID getMobBredWith() {
return entityData.get(DATA_MOB_BRED_WITH).orElse(null);
}
// Goals
class RoosterMeleeAttackGoal extends MeleeAttackGoal {
RoosterMeleeAttackGoal() {
super(Rooster.this, 1.4, true);
}
@Override
public void tick() {
super.tick();
if (mob.getTarget() != null && mob.distanceToSqr(mob.getTarget()) <= 4) mob.getJumpControl().jump();
}
}
class RoosterRandomAttackNearbyRoosterGoal extends NearestAttackableTargetGoal<Rooster> {
RoosterRandomAttackNearbyRoosterGoal() {
super(Rooster.this, Rooster.class, true);
}
@Override
public boolean canUse() {
return super.canUse() && random.nextInt(300) == 0;
}
@Override
public boolean canContinueToUse() {
return super.canContinueToUse() && mob.getLastHurtMob() != targetMob;
}
}
class RoosterBreedGoal extends BreedGoal {
RoosterBreedGoal() {
super(Rooster.this, 1, Chicken.class);
}
@Override
protected void breed() {
ServerPlayer serverplayer = animal.getLoveCause();
if (serverplayer == null && this.partner.getLoveCause() != null) {
serverplayer = this.partner.getLoveCause();
}
if (serverplayer != null) {
serverplayer.awardStat(Stats.ANIMALS_BRED);
CriteriaTriggers.BRED_ANIMALS.trigger(serverplayer, this.animal, this.partner, null);
}
((BredWithMobStorable) partner).setMobBredWith(animal.getUUID());
((BredWithMobStorable) animal).setMobBredWith(partner.getUUID());
animal.setAge(6000);
partner.setAge(6000);
animal.resetLove();
partner.resetLove();
RandomSource randomsource = animal.getRandom();
if (this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) {
this.level.addFreshEntity(new ExperienceOrb(this.level,
this.animal.getX(),
this.animal.getY(),
this.animal.getZ(),
randomsource.nextInt(7) + 1));
}
}
}
private static final DelayedTask<Rooster> CROW_LATER = new DelayedTask<>(30) {
@Override
protected void execute() {
entity.crow();
}
};
private static final DelayedTask<Rooster> ANGRY_CLUCK_LATER = new DelayedTask<>(30) {
@Override
protected void execute() {
entity.playSound(_Sounds.ROOSTER_ANGRY_CLUCK.get());
}
};
@Override
public ImmutableList<DelayedTask<Rooster>> getTasks() {
return ImmutableList.of(CROW_LATER, ANGRY_CLUCK_LATER);
}
class RoosterRandomTryFindFoodGoal extends Goal {
private final Rooster rooster;
private int tickCount;
private int maxTickCount;
private int lastDugTicks;
private int roosterLastHurtByMobTimestamp;
private double lookAtXOld,
lookAtYOld,
lookAtZOld;
private Vec3 pos;
private boolean didEndSuccessfully;
private Vec3 lookingAtPos;
private static final UniformInt POSSIBLE_MAX_TICK_COUNT
= TimeUtil.rangeOfSeconds(4, 10);
RoosterRandomTryFindFoodGoal() {
super();
this.rooster = Rooster.this;
setFlags(EnumSet.of(Flag.LOOK));
}
@Override
public boolean canUse() {
return (isStandingOn(Blocks.GRASS_BLOCK)
|| isStandingOn(Blocks.DIRT)
|| isStandingOn(Blocks.PODZOL))
&& rooster.random.nextInt(3000) == 20;
}
@Override
public boolean canContinueToUse() {
if (rooster.getLastHurtByMobTimestamp() != roosterLastHurtByMobTimestamp) {
didEndSuccessfully = false;
return false;
} else if (tickCount >= maxTickCount) {
didEndSuccessfully = true;
return false;
}
return true;
}
@Override
public boolean requiresUpdateEveryTick() {
return true;
}
@Override
public void start() {
lastDugTicks = 20;
roosterLastHurtByMobTimestamp = rooster.getLastHurtByMobTimestamp();
lookAtXOld = rooster.getLookControl().getWantedX();
lookAtYOld = rooster.getLookControl().getWantedY();
lookAtZOld = rooster.getLookControl().getWantedZ();
pos = new Vec3(rooster.position().toVector3f());
lookingAtPos = rooster.position().subtract(0, 1, 0);
rooster.getLookControl().setLookAt(lookingAtPos);
maxTickCount = POSSIBLE_MAX_TICK_COUNT.sample(rooster.random);
}
@Override
public void stop() {
rooster.getLookControl().setLookAt(lookAtXOld, lookAtYOld, lookAtZOld);
if (didEndSuccessfully) {
int resultItemPercentage = rooster.random.nextInt(100);
if (resultItemPercentage <= 1) {
rooster.spawnAtLocation(Items.DIAMOND, -1);
Rooster.ANGRY_CLUCK_LATER.startExecution(rooster);
} else if (resultItemPercentage <= 5) {
rooster.spawnAtLocation(Items.GOLD_NUGGET, -1);
Rooster.ANGRY_CLUCK_LATER.startExecution(rooster);
} else if (resultItemPercentage <= 20) {
Rooster.ANGRY_CLUCK_LATER.startExecution(rooster);
} else {
rooster.spawnAtLocation(FOOD_ITEMS.getItems()[rooster.random.nextInt(
FOOD_ITEMS.getItems().length)], -1);
Rooster.CROW_LATER.startExecution(rooster);
}
}
}
@Override
public void tick() {
++tickCount;
rooster.getLookControl().setLookAt(lookingAtPos);
rooster.getMoveControl().setWantedPosition(pos.x, pos.y, pos.z, rooster.getMoveControl().getSpeedModifier());
if (lastDugTicks <= 0) {
lastDugTicks = 20;
rooster.diggingAnimTicks = 10;
rooster.playSound(rooster.level().getBlockState(rooster.blockPosition().below())
.getSoundType(rooster.level(), rooster.blockPosition().below(), rooster)
.getHitSound());
} else lastDugTicks--;
}
private boolean isStandingOn(Block block) {
return rooster.level().getBlockState(rooster.blockPosition().below()).is(block);
}
}
// Feather data storage
@Immutable
public record FourFeathers(@NotNull FeatherType first,
@NotNull FeatherType second,
@NotNull FeatherType third,
@NotNull FeatherType fourth)
{
public static final FourFeathers NONE = new FourFeathers(FeatherType.NONE, FeatherType.NONE,
FeatherType.NONE, FeatherType.NONE);
public FourFeathers(byte first, byte second, byte third, byte forth) {
this(FeatherType.fromByte(first),
FeatherType.fromByte(second),
FeatherType.fromByte(third),
FeatherType.fromByte(forth));
}
public @NotNull Pair<ItemStack, FourFeathers> removeRandom(RandomSource randomSource) {
if (NONE.equals(this)) return new Pair<>(ItemStack.EMPTY, this);
int randomNum = randomSource.nextInt(4);
while (get(randomNum) == null) randomNum = randomSource.nextInt(4);
ItemStack retVal0 = get(randomNum).getDefaultInstance();
FourFeathers retVal1 = switch (randomNum) {
case 0 -> new FourFeathers(FeatherType.NONE, second, third, fourth);
case 1 -> new FourFeathers(first, FeatherType.NONE, third, fourth);
case 2 -> new FourFeathers(first, second, FeatherType.NONE, fourth);
case 3 -> new FourFeathers(first, second, third, FeatherType.NONE);
default -> null; // unreachable
};
return new Pair<>(retVal0, retVal1);
}
public void dropAll(Rooster asRooster) {
asRooster.setFeatherData(FourFeathers.NONE);
if (first != FeatherType.NONE) asRooster.spawnAtLocation(first);
if (second != FeatherType.NONE) asRooster.spawnAtLocation(second);
if (third != FeatherType.NONE) asRooster.spawnAtLocation(third);
if (fourth != FeatherType.NONE) asRooster.spawnAtLocation(fourth);
}
@Nullable
public Item get(int i) {
return (switch (i) {
case 0 -> first;
case 1 -> second;
case 2 -> third;
case 3 -> fourth;
default -> throw new IllegalStateException("Unexpected value: " + i);
}).asItem();
}
// NBT Utils
@NotNull
public CompoundTag writeTag() {
CompoundTag nbt = new CompoundTag();
nbt.putString("first", first.name().toLowerCase());
nbt.putString("second", second.name().toLowerCase());
nbt.putString("third", third.name().toLowerCase());
nbt.putString("fourth", fourth.name().toLowerCase());
return nbt;
}
@NotNull
public static FourFeathers readTag(@NotNull CompoundTag nbt) {
return new FourFeathers(tryRead(nbt, "first"),
tryRead(nbt, "second"),
tryRead(nbt, "third"),
tryRead(nbt, "fourth"));
}
private static FeatherType tryRead(CompoundTag tag, String toRead) {
if (tag.contains(toRead, Tag.TAG_STRING)) {
try {
return FeatherType.valueOf(tag.getString(toRead).toUpperCase());
} catch (IllegalArgumentException e) {
return FeatherType.NONE;
}
}
return FeatherType.NONE;
}
public FourFeathers copy() {
return new FourFeathers(first, second, third, fourth);
}
}
public enum FeatherType implements ItemLike {
RED(_Items.RED_FEATHER, (byte) 0),
YELLOW(_Items.YELLOW_FEATHER, (byte) 1),
GREEN(_Items.GREEN_FEATHER, (byte) 2),
BLUE(_Items.BLUE_FEATHER, (byte) 3),
NONE(() -> null, (byte) -1);
private final Supplier<Item> itemSup;
private final byte byteVal;
FeatherType(Supplier<Item> itemSup, byte byteVal) {
this.itemSup = itemSup;
this.byteVal = byteVal;
}
public static FeatherType fromByte(byte item) {
return switch (item) {
case -1 -> NONE;
case 0 -> RED;
case 1 -> YELLOW;
case 2 -> GREEN;
case 3 -> BLUE;
default -> throw new IllegalArgumentException("item: " + item);
};
}
@Override
public @Nullable Item asItem() {
return itemSup.get();
}
public byte asByte() {
return byteVal;
}
}
@Override
protected void dropCustomDeathLoot(@NotNull DamageSource damageSource, int what, boolean idk) {
super.dropCustomDeathLoot(damageSource, what, idk);
getFeatherData().dropAll(this);
}
// Sounds
@Override
protected SoundEvent getAmbientSound() {
return _Sounds.ROOSTER_AMBIENT.get();
}
@Override
protected SoundEvent getHurtSound(@NotNull DamageSource damageSource) {
return _Sounds.ROOSTER_HURT.get();
}
@Override
protected SoundEvent getDeathSound() {
return _Sounds.ROOSTER_DEATH.get();
}
public static AttributeSupplier.Builder createAttributes() {
return createMobAttributes()
.add(Attributes.ATTACK_DAMAGE, 2)
.add(Attributes.MAX_HEALTH, 8)
.add(Attributes.MOVEMENT_SPEED, .3);
}
@Override
public boolean isBaby() {
return false;
}
@Override
public boolean canMate(@NotNull Animal other) {
if (this == other) return false;
else if (!(other instanceof Chicken)) return false;
else return isInLove() && other.isInLove();
}
@Override
@Nullable
public SpawnGroupData finalizeSpawn(@NotNull ServerLevelAccessor worldAccessor,
@NotNull DifficultyInstance difficulty,
@NotNull MobSpawnType mobSpawnType,
@Nullable SpawnGroupData retVal,
@Nullable CompoundTag nbt) {
if (nbt == null) {
setFeatherData(new FourFeathers((byte) random.nextIntBetweenInclusive(-1, 3),
(byte) random.nextIntBetweenInclusive(-1, 3),
(byte) random.nextIntBetweenInclusive(-1, 3),
(byte) random.nextIntBetweenInclusive(-1, 3)));
} else {
if (nbt.contains("FeatherData", Tag.TAG_COMPOUND)) {
setFeatherData(FourFeathers.readTag(nbt.getCompound("FeatherData")));
}
}
return super.finalizeSpawn(worldAccessor, difficulty, mobSpawnType, retVal, nbt);
}
@Override
protected void doAIStep() {
if (crowing && --crowTicks <= 0) {
crowing = false;
}
if (!level().isClientSide) {
updatePersistentAnger((ServerLevel) level(), true);
if (level().getDayTime() % 24000L == 0L || level().getDayTime() % 24000L == 13000L)
crow();
}
}
@Override
public void setBaby(boolean baby) {
if (baby) {
Chick me = convertTo(_Entities.CHICK.get(), false);
if (me != null) {
me.setBoy(true);
}
}
}
@Override
public @NotNull InteractionResult mobInteract(@NotNull Player player, @NotNull InteractionHand hand) {
if (isFood(player.getItemInHand(hand))) {
return super.mobInteract(player, hand);
} else if (player.getItemInHand(hand) == ItemStack.EMPTY) {
dropFeather(player);
return InteractionResult.CONSUME;
}
return InteractionResult.PASS;
}
private void dropFeather(@Nullable LivingEntity target) {
Pair<ItemStack, FourFeathers> resFeathers = getFeatherData().removeRandom(random);
setFeatherData(resFeathers.getB());
if (resFeathers.getA() != null && featherDropCoolDown == 0 && target != null) {
spawnAtLocation(resFeathers.getA());
playSound(SoundEvents.ITEM_PICKUP);
setTarget(target);
featherDropCoolDown = random.nextIntBetweenInclusive(20, 30);
}
}
@Override
public void setTarget(@Nullable LivingEntity target) {
super.setTarget(target);
if (target != null) {
setPersistentAngerTarget(target.getUUID());
startPersistentAngerTimer();
}
}
public void crow() {
if (!crowing) {
crowing = true;
crowTicks = 70;
var lookControl = getLookControl();
lookControl.setLookAt(lookControl.getWantedX(), position().y + .2, lookControl.getWantedZ());
playSound(_Sounds.ROOSTER_CROW.get());
level().getEntitiesOfClass(Chicken.class, getBoundingBox().inflate(20))
.forEach(chicken -> chicken.setTarget(this));
}
}
public boolean isCrowing() {
return crowing;
}
@Override
public void tick() {
super.tick();
if (diggingAnimTicks > 0) --diggingAnimTicks;
if (featherDropCoolDown > 0) --featherDropCoolDown;
tickTasks();
}
public int getDiggingAnimTicks() {
return diggingAnimTicks;
}
@Override
public boolean isBoy() {
return true;
}
}
package mymod.entities.chicken;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.*;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.goal.*;
import net.minecraft.world.entity.animal.Animal;
import net.minecraft.world.entity.animal.Chicken;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.phys.AABB;
import org.jetbrains.annotations.NotNull;
import mymod._Entities;
import mymod._Sounds;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class Chick extends AbstractChicken {
private static final EntityDataAccessor<Boolean> DATA_IS_BOY
= SynchedEntityData.defineId(Chick.class, EntityDataSerializers.BOOLEAN);
private final boolean initializedIsBoy;
public Chick(EntityType<? extends Chick> entityType, Level world) {
super(entityType, world);
setAge(-24000);
initializedIsBoy = false;
}
public Chick(EntityType<? extends Chick> entityType, Level world, boolean isBoy) {
super(entityType, world);
setAge(-24000);
setBoy(isBoy);
initializedIsBoy = true;
}
@Override
protected SoundEvent getAmbientSound() {
return _Sounds.CHICK_AMBIENT.get();
}
@Override
protected SoundEvent getHurtSound(@NotNull DamageSource damageSource) {
return _Sounds.CHICK_HURT.get();
}
@Override
protected SoundEvent getDeathSound() {
return _Sounds.CHICK_DEATH.get();
}
@Override
public float getVoicePitch() {
return (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F;
}
@Override
protected void defineSynchedData() {
super.defineSynchedData();
entityData.define(DATA_IS_BOY, false);
}
@Override
public void addAdditionalSaveData(@NotNull CompoundTag nbt) {
super.addAdditionalSaveData(nbt);
nbt.putBoolean("IsBoy", isBoy());
}
@Override
public void readAdditionalSaveData(@NotNull CompoundTag nbt) {
super.readAdditionalSaveData(nbt);
if (nbt.contains("IsBoy", Tag.TAG_BYTE)) setBoy(nbt.getBoolean("IsBoy"));
}
@Override
protected void registerGoals() {
goalSelector.addGoal(0, new FloatGoal(this));
goalSelector.addGoal(1, new PanicGoal(this, 1.4));
goalSelector.addGoal(2, new TemptGoal(this, 1, FOOD_ITEMS, false));
goalSelector.addGoal(3, new ChickFollowParentGoal());
goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1));
goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6));
goalSelector.addGoal(7, new RandomLookAroundGoal(this));
}
@Override
public void setLastHurtByMob(LivingEntity hurtByMob) {
super.setLastHurtByMob(hurtByMob);
if (getType() == _Entities.CHICK.get() && hurtByMob != null) {
level().getEntitiesOfClass(Rooster.class,
new AABB(blockPosition().below(20).north(20).west(20),
blockPosition().above(20).south(20).east(20)))
.stream()
.filter(r -> r.getPersistentAngerTarget() == null && r.getTarget() == null)
.forEach(r -> r.setTarget(hurtByMob));
}
}
@Override
public SpawnGroupData finalizeSpawn(@NotNull ServerLevelAccessor worldAccessor,
@NotNull DifficultyInstance difficulty,
@NotNull MobSpawnType mobSpawnType,
@Nullable SpawnGroupData retVal,
@Nullable CompoundTag nbt) {
if (!initializedIsBoy) {
if (nbt != null && nbt.contains("IsBoy", Tag.TAG_BYTE))
setBoy(nbt.getBoolean("IsBoy"));
else
setBoy(random.nextBoolean());
}
return super.finalizeSpawn(worldAccessor, difficulty, mobSpawnType, retVal, nbt);
}
class ChickFollowParentGoal extends Goal {
private Animal parent;
private int timeToRecalculatePath;
@Override
public boolean canUse() {
List<Animal> list = new ArrayList<>();
list.addAll(level().getEntitiesOfClass(Chicken.class,
getBoundingBox().inflate(8, 4, 8)));
list.addAll(level().getEntitiesOfClass(Rooster.class,
getBoundingBox().inflate(8, 4, 8)));
Animal animal = null;
double d0 = Double.MAX_VALUE;
for (Animal animal1 : list) {
if (animal1.getAge() >= 0) {
double d1 = distanceToSqr(animal1);
if (!(d1 > d0)) {
d0 = d1;
animal = animal1;
}
}
}
if (animal == null) {
return false;
} else if (d0 < 9.0D) {
return false;
} else {
parent = animal;
return true;
}
}
@Override
public boolean canContinueToUse() {
if (!parent.isAlive()) return false;
double d = distanceToSqr(parent);
return !(d < 9) && !(d > 256);
}
@Override
public void start() {
timeToRecalculatePath = 0;
}
@Override
public void stop() {
parent = null;
}
@Override
public void tick() {
if (--timeToRecalculatePath <= 0) {
timeToRecalculatePath = adjustedTickDelay(10);
getNavigation().moveTo(parent, 1.1);
}
}
}
public static AttributeSupplier.Builder createAttributes() {
return createMobAttributes()
.add(Attributes.MAX_HEALTH, 4)
.add(Attributes.MOVEMENT_SPEED, .25);
}
@Override
public void ageBoundaryReached() {
super.ageBoundaryReached();
if (getAge() >= 0) {
if (isBoy())
convertTo(_Entities.ROOSTER.get(), false);
else
convertTo(EntityType.CHICKEN, false);
}
}
@Override
public boolean isBaby() {
return true;
}
@Override
public boolean canMate(@NotNull Animal other) {
return false;
}
@Override
public void setBoy(boolean newBoolean) {
entityData.set(DATA_IS_BOY, newBoolean);
}
@Override
protected void doAIStep() {
}
@Override
public void setBaby(boolean baby) {
if (!baby) {
if (isBoy()) {
convertTo(_Entities.ROOSTER.get(), false);
} else {
convertTo(EntityType.CHICKEN, false);
}
}
}
@Override
public boolean isBoy() {
return entityData.get(DATA_IS_BOY);
}
}
sorry for too long post lol