Jump to content

[1.20.1] Why my custom entity thinks that air is food


Recommended Posts

Posted (edited)

I checked my isFood() method:

@Override
public boolean isFood(@NotNull ItemStack item) {
    return FOOD_ITEMS.test(item);
}

the FOOD_ITEMS:

protected static final Ingredient FOOD_ITEMS // sorry for protected static lol
            = Ingredient.of(Items.WHEAT_SEEDS,
            Items.MELON_SEEDS, Items.PUMPKIN_SEEDS,
            Items.BEETROOT_SEEDS, Items.TORCHFLOWER_SEEDS, Items.PITCHER_POD);

and the Ingredient class, but nothing seems to tell Minecraft that air is a food item. 

Tell me if you need full entity code

Edited by btuh
Posted

Because air is good! :D Jokes aside, how can you tell that your mob is eating air? Like, there's an actual action in game that you can observe? Also show the full entity code, maybe there's something weird going on there as well

 

PS: don't bump posts, someone will eventually show up :D You should also consider asking questions in the discord server, wherre is more likely that you'll get a quick response

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted
37 minutes ago, JimiIT92 said:

Because air is good! :D Jokes aside, how can you tell that your mob is eating air? Like, there's an actual action in game that you can observe? Also show the full entity code, maybe there's something weird going on there as well

 

PS: don't bump posts, someone will eventually show up :D You should also consider asking questions in the discord server, wherre is more likely that you'll get a quick response

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

Posted (edited)

It might just be a misleading log, if you run the command while debugging what's the outcome? 
I've tried checking the item inside the RightClickBlock event and it works just fine

private 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);

@SubscribeEvent
public static void onRightClickBlock(final PlayerInteractEvent.RightClickBlock event) {
	final ItemStack itemStack = event.getItemStack();
    LOGGER.info(FOOD_ITEMS.test(itemStack) + "");
}

When I right click a block with an Item it checks if is one of the ingredients of FOOD_ITEMS. Clicking with an empty hand (air) logs false, as expected.

Tip: please use the "spoiler" tags for long code snippets and don't merge all your classes into one code snippet

EDIT: just noticed you are on 1.20.1. Maybe is just a bug with the Forge version? Try update to the latest Forge 1.20.2 and see if it still occurs

Edited by JimiIT92

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted (edited)
23 hours ago, JimiIT92 said:

It might just be a misleading log, if you run the command while debugging what's the outcome? 
I've tried checking the item inside the RightClickBlock event and it works just fine

private 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);

@SubscribeEvent
public static void onRightClickBlock(final PlayerInteractEvent.RightClickBlock event) {
	final ItemStack itemStack = event.getItemStack();
    LOGGER.info(FOOD_ITEMS.test(itemStack) + "");
}

When I right click a block with an Item it checks if is one of the ingredients of FOOD_ITEMS. Clicking with an empty hand (air) logs false, as expected.

Tip: please use the "spoiler" tags for long code snippets and don't merge all your classes into one code snippet

EDIT: just noticed you are on 1.20.1. Maybe is just a bug with the Forge version? Try update to the latest Forge 1.20.2 and see if it still occurs

I didn't yet see this happening on 1.20.2 forge, but sadly, I noticed that my mod is incompatible with this version because of this event handler:

@SubscribeEvent
    public static void onPlayerRightClickItem(PlayerInteractEvent.RightClickItem e) {
        if (e.getItemStack().getItem() instanceof EggItem) {
            List<ThrownEgg> eggs = e.getLevel().getEntitiesOfClass(ThrownEgg.class,
                    new AABB(e.getEntity().blockPosition().immutable()
                            .below(5)
                            .north(5)
                            .west(5),
                            e.getEntity().blockPosition().immutable()
                                    .above(10)
                                    .south(5)
                                    .east(5)));
            if (!eggs.isEmpty()) {
                eggs.forEach(ThrownEgg::discard);
                if (!e.getEntity().getAbilities().instabuild)
                    e.getItemStack().grow(1);
            }


            if (!e.getEntity().level().isClientSide) {
                ((ServerPlayer) e.getEntity()).connection.send(
                        new ClientboundStopSoundPacket(SoundEvents.EGG_THROW.getLocation(),
                                SoundSource.PLAYERS)); // Minecraft Forge 1.20.2 crashes because of NoSuchMethodError here
            }
            e.setCancellationResult(InteractionResult.SUCCESS);
            e.setCanceled(true);
        }
    }

 

Edited by btuh
Posted

Also why roosters don't spawn naturally

Shouldn't this do the thing?

// CommonModEvents
@SubscribeEvent
public static void registerSpawnPlacements(SpawnPlacementRegisterEvent e) {
    e.register(_Entities.ROOSTER.get(), ON_GROUND, WORLD_SURFACE, AbstractChicken::canSpawn, AND);
}

 

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);
}

 

 

 

Posted (edited)
On 12/5/2023 at 2:13 PM, JimiIT92 said:

It might just be a misleading log, if you run the command while debugging what's the outcome? 
I've tried checking the item inside the RightClickBlock event and it works just fine

private 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);

@SubscribeEvent
public static void onRightClickBlock(final PlayerInteractEvent.RightClickBlock event) {
	final ItemStack itemStack = event.getItemStack();
    LOGGER.info(FOOD_ITEMS.test(itemStack) + "");
}

When I right click a block with an Item it checks if is one of the ingredients of FOOD_ITEMS. Clicking with an empty hand (air) logs false, as expected.

Tip: please use the "spoiler" tags for long code snippets and don't merge all your classes into one code snippet

EDIT: just noticed you are on 1.20.1. Maybe is just a bug with the Forge version? Try update to the latest Forge 1.20.2 and see if it still occurs

Okay it doesn't happen anymore I think, but now I have two more problems that I posted before this one

Edited by btuh
Posted
On 12/5/2023 at 12:45 PM, btuh said:
((ServerPlayer) e.getEntity()).connection.send(
                        new ClientboundStopSoundPacket(SoundEvents.EGG_THROW.getLocation(),
                                SoundSource.PLAYERS)); // Minecraft Forge 1.20.2 crashes because of NoSuchMethodError here

Why are you doing this instead of just playing the sound using the level or the player method? 

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted
On 12/8/2023 at 2:22 AM, JimiIT92 said:

Why are you doing this instead of just playing the sound using the level or the player method? 

I'm stopping it (as it's done it StopsoundCommand class)

Posted
On 12/5/2023 at 2:13 PM, JimiIT92 said:

It might just be a misleading log, if you run the command while debugging what's the outcome? 
I've tried checking the item inside the RightClickBlock event and it works just fine

private 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);

@SubscribeEvent
public static void onRightClickBlock(final PlayerInteractEvent.RightClickBlock event) {
	final ItemStack itemStack = event.getItemStack();
    LOGGER.info(FOOD_ITEMS.test(itemStack) + "");
}

When I right click a block with an Item it checks if is one of the ingredients of FOOD_ITEMS. Clicking with an empty hand (air) logs false, as expected.

Tip: please use the "spoiler" tags for long code snippets and don't merge all your classes into one code snippet

EDIT: just noticed you are on 1.20.1. Maybe is just a bug with the Forge version? Try update to the latest Forge 1.20.2 and see if it still occurs

I tried it, and when roosters started following me, I clicked a block with empty hand and it logged (air false). So the Ingredient class is totally not the issue, I guess so.

Posted
22 hours ago, btuh said:

I'm stopping it (as it's done it StopsoundCommand class)

Ah I see, haven't noticed that 😅

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted
On 12/5/2023 at 7:11 PM, btuh said:

Also why roosters don't spawn naturally

Shouldn't this do the thing?

// CommonModEvents
@SubscribeEvent
public static void registerSpawnPlacements(SpawnPlacementRegisterEvent e) {
    e.register(_Entities.ROOSTER.get(), ON_GROUND, WORLD_SURFACE, AbstractChicken::canSpawn, AND);
}

 

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);
}

 

 

 

Have you ever had issues with this?

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.