minecraft-src/net/minecraft/world/entity/animal/sheep/Sheep.java
2025-07-04 03:45:38 +03:00

314 lines
11 KiB
Java

package net.minecraft.world.entity.animal.sheep;
import java.util.Map;
import net.minecraft.Util;
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.nbt.CompoundTag;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.tags.ItemTags;
import net.minecraft.util.ARGB;
import net.minecraft.util.Mth;
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.AgeableMob;
import net.minecraft.world.entity.EntitySpawnReason;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.Shearable;
import net.minecraft.world.entity.SpawnGroupData;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier.Builder;
import net.minecraft.world.entity.ai.goal.BreedGoal;
import net.minecraft.world.entity.ai.goal.EatBlockGoal;
import net.minecraft.world.entity.ai.goal.FloatGoal;
import net.minecraft.world.entity.ai.goal.FollowParentGoal;
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
import net.minecraft.world.entity.ai.goal.PanicGoal;
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
import net.minecraft.world.entity.ai.goal.TemptGoal;
import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal;
import net.minecraft.world.entity.animal.Animal;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.storage.loot.BuiltInLootTables;
import org.jetbrains.annotations.Nullable;
public class Sheep extends Animal implements Shearable {
private static final int EAT_ANIMATION_TICKS = 40;
private static final EntityDataAccessor<Byte> DATA_WOOL_ID = SynchedEntityData.defineId(Sheep.class, EntityDataSerializers.BYTE);
private static final Map<DyeColor, Integer> COLOR_BY_DYE = Util.makeEnumMap(DyeColor.class, Sheep::createSheepColor);
private static final DyeColor DEFAULT_COLOR = DyeColor.WHITE;
private static final boolean DEFAULT_SHEARED = false;
private int eatAnimationTick;
private EatBlockGoal eatBlockGoal;
private static int createSheepColor(DyeColor color) {
if (color == DyeColor.WHITE) {
return -1644826;
} else {
int i = color.getTextureDiffuseColor();
float f = 0.75F;
return ARGB.color(255, Mth.floor(ARGB.red(i) * 0.75F), Mth.floor(ARGB.green(i) * 0.75F), Mth.floor(ARGB.blue(i) * 0.75F));
}
}
public static int getColor(DyeColor color) {
return (Integer)COLOR_BY_DYE.get(color);
}
public Sheep(EntityType<? extends Sheep> entityType, Level level) {
super(entityType, level);
}
@Override
protected void registerGoals() {
this.eatBlockGoal = new EatBlockGoal(this);
this.goalSelector.addGoal(0, new FloatGoal(this));
this.goalSelector.addGoal(1, new PanicGoal(this, 1.25));
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0));
this.goalSelector.addGoal(3, new TemptGoal(this, 1.1, itemStack -> itemStack.is(ItemTags.SHEEP_FOOD), false));
this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.1));
this.goalSelector.addGoal(5, this.eatBlockGoal);
this.goalSelector.addGoal(6, new WaterAvoidingRandomStrollGoal(this, 1.0));
this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 6.0F));
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
}
@Override
public boolean isFood(ItemStack stack) {
return stack.is(ItemTags.SHEEP_FOOD);
}
@Override
protected void customServerAiStep(ServerLevel level) {
this.eatAnimationTick = this.eatBlockGoal.getEatAnimationTick();
super.customServerAiStep(level);
}
@Override
public void aiStep() {
if (this.level().isClientSide) {
this.eatAnimationTick = Math.max(0, this.eatAnimationTick - 1);
}
super.aiStep();
}
public static Builder createAttributes() {
return Animal.createAnimalAttributes().add(Attributes.MAX_HEALTH, 8.0).add(Attributes.MOVEMENT_SPEED, 0.23F);
}
@Override
protected void defineSynchedData(net.minecraft.network.syncher.SynchedEntityData.Builder builder) {
super.defineSynchedData(builder);
builder.define(DATA_WOOL_ID, (byte)0);
}
@Override
public void handleEntityEvent(byte id) {
if (id == 10) {
this.eatAnimationTick = 40;
} else {
super.handleEntityEvent(id);
}
}
public float getHeadEatPositionScale(float partialTick) {
if (this.eatAnimationTick <= 0) {
return 0.0F;
} else if (this.eatAnimationTick >= 4 && this.eatAnimationTick <= 36) {
return 1.0F;
} else {
return this.eatAnimationTick < 4 ? (this.eatAnimationTick - partialTick) / 4.0F : -(this.eatAnimationTick - 40 - partialTick) / 4.0F;
}
}
public float getHeadEatAngleScale(float partialTick) {
if (this.eatAnimationTick > 4 && this.eatAnimationTick <= 36) {
float f = (this.eatAnimationTick - 4 - partialTick) / 32.0F;
return (float) (Math.PI / 5) + 0.21991149F * Mth.sin(f * 28.7F);
} else {
return this.eatAnimationTick > 0 ? (float) (Math.PI / 5) : this.getXRot(partialTick) * (float) (Math.PI / 180.0);
}
}
@Override
public InteractionResult mobInteract(Player player, InteractionHand hand) {
ItemStack itemStack = player.getItemInHand(hand);
if (itemStack.is(Items.SHEARS)) {
if (this.level() instanceof ServerLevel serverLevel && this.readyForShearing()) {
this.shear(serverLevel, SoundSource.PLAYERS, itemStack);
this.gameEvent(GameEvent.SHEAR, player);
itemStack.hurtAndBreak(1, player, getSlotForHand(hand));
return InteractionResult.SUCCESS_SERVER;
} else {
return InteractionResult.CONSUME;
}
} else {
return super.mobInteract(player, hand);
}
}
@Override
public void shear(ServerLevel level, SoundSource soundSource, ItemStack shears) {
level.playSound(null, this, SoundEvents.SHEEP_SHEAR, soundSource, 1.0F, 1.0F);
this.dropFromShearingLootTable(
level,
BuiltInLootTables.SHEAR_SHEEP,
shears,
(serverLevel, itemStack) -> {
for (int i = 0; i < itemStack.getCount(); i++) {
ItemEntity itemEntity = this.spawnAtLocation(serverLevel, itemStack.copyWithCount(1), 1.0F);
if (itemEntity != null) {
itemEntity.setDeltaMovement(
itemEntity.getDeltaMovement()
.add(
(this.random.nextFloat() - this.random.nextFloat()) * 0.1F,
this.random.nextFloat() * 0.05F,
(this.random.nextFloat() - this.random.nextFloat()) * 0.1F
)
);
}
}
}
);
this.setSheared(true);
}
@Override
public boolean readyForShearing() {
return this.isAlive() && !this.isSheared() && !this.isBaby();
}
@Override
public void addAdditionalSaveData(CompoundTag tag) {
super.addAdditionalSaveData(tag);
tag.putBoolean("Sheared", this.isSheared());
tag.store("Color", DyeColor.LEGACY_ID_CODEC, this.getColor());
}
@Override
public void readAdditionalSaveData(CompoundTag tag) {
super.readAdditionalSaveData(tag);
this.setSheared(tag.getBooleanOr("Sheared", false));
this.setColor((DyeColor)tag.read("Color", DyeColor.LEGACY_ID_CODEC).orElse(DEFAULT_COLOR));
}
@Override
protected SoundEvent getAmbientSound() {
return SoundEvents.SHEEP_AMBIENT;
}
@Override
protected SoundEvent getHurtSound(DamageSource damageSource) {
return SoundEvents.SHEEP_HURT;
}
@Override
protected SoundEvent getDeathSound() {
return SoundEvents.SHEEP_DEATH;
}
@Override
protected void playStepSound(BlockPos pos, BlockState state) {
this.playSound(SoundEvents.SHEEP_STEP, 0.15F, 1.0F);
}
public DyeColor getColor() {
return DyeColor.byId(this.entityData.get(DATA_WOOL_ID) & 15);
}
public void setColor(DyeColor color) {
byte b = this.entityData.get(DATA_WOOL_ID);
this.entityData.set(DATA_WOOL_ID, (byte)(b & 240 | color.getId() & 15));
}
@Nullable
@Override
public <T> T get(DataComponentType<? extends T> component) {
return component == DataComponents.SHEEP_COLOR ? castComponentValue((DataComponentType<T>)component, this.getColor()) : super.get(component);
}
@Override
protected void applyImplicitComponents(DataComponentGetter componentGetter) {
this.applyImplicitComponentIfPresent(componentGetter, DataComponents.SHEEP_COLOR);
super.applyImplicitComponents(componentGetter);
}
@Override
protected <T> boolean applyImplicitComponent(DataComponentType<T> component, T value) {
if (component == DataComponents.SHEEP_COLOR) {
this.setColor(castComponentValue(DataComponents.SHEEP_COLOR, value));
return true;
} else {
return super.applyImplicitComponent(component, value);
}
}
public boolean isSheared() {
return (this.entityData.get(DATA_WOOL_ID) & 16) != 0;
}
public void setSheared(boolean sheared) {
byte b = this.entityData.get(DATA_WOOL_ID);
if (sheared) {
this.entityData.set(DATA_WOOL_ID, (byte)(b | 16));
} else {
this.entityData.set(DATA_WOOL_ID, (byte)(b & -17));
}
}
public static DyeColor getRandomSheepColor(ServerLevelAccessor level, BlockPos pos) {
Holder<Biome> holder = level.getBiome(pos);
return SheepColorSpawnRules.getSheepColor(holder, level.getRandom());
}
@Nullable
public Sheep getBreedOffspring(ServerLevel serverLevel, AgeableMob ageableMob) {
Sheep sheep = EntityType.SHEEP.create(serverLevel, EntitySpawnReason.BREEDING);
if (sheep != null) {
DyeColor dyeColor = this.getColor();
DyeColor dyeColor2 = ((Sheep)ageableMob).getColor();
sheep.setColor(DyeColor.getMixedColor(serverLevel, dyeColor, dyeColor2));
}
return sheep;
}
@Override
public void ate() {
super.ate();
this.setSheared(false);
if (this.isBaby()) {
this.ageUp(60);
}
}
@Nullable
@Override
public SpawnGroupData finalizeSpawn(
ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData
) {
this.setColor(getRandomSheepColor(level, this.blockPosition()));
return super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData);
}
}