package net.minecraft.world.entity.animal; import io.netty.buffer.ByteBuf; import java.util.function.IntFunction; 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.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.network.syncher.SynchedEntityData.Builder; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.util.ByIdMap; import net.minecraft.util.StringRepresentable; import net.minecraft.util.ByIdMap.OutOfBoundsStrategy; import net.minecraft.util.random.WeightedList; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Pose; import net.minecraft.world.entity.SpawnGroupData; 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 org.jetbrains.annotations.Nullable; public class Salmon extends AbstractSchoolingFish { private static final String TAG_TYPE = "type"; private static final EntityDataAccessor DATA_TYPE = SynchedEntityData.defineId(Salmon.class, EntityDataSerializers.INT); public Salmon(EntityType entityType, Level level) { super(entityType, level); this.refreshDimensions(); } @Override public int getMaxSchoolSize() { return 5; } @Override public ItemStack getBucketItemStack() { return new ItemStack(Items.SALMON_BUCKET); } @Override protected SoundEvent getAmbientSound() { return SoundEvents.SALMON_AMBIENT; } @Override protected SoundEvent getDeathSound() { return SoundEvents.SALMON_DEATH; } @Override protected SoundEvent getHurtSound(DamageSource damageSource) { return SoundEvents.SALMON_HURT; } @Override protected SoundEvent getFlopSound() { return SoundEvents.SALMON_FLOP; } @Override protected void defineSynchedData(Builder builder) { super.defineSynchedData(builder); builder.define(DATA_TYPE, Salmon.Variant.DEFAULT.id()); } @Override public void onSyncedDataUpdated(EntityDataAccessor dataAccessor) { super.onSyncedDataUpdated(dataAccessor); if (DATA_TYPE.equals(dataAccessor)) { this.refreshDimensions(); } } @Override public void addAdditionalSaveData(CompoundTag tag) { super.addAdditionalSaveData(tag); tag.store("type", Salmon.Variant.CODEC, this.getVariant()); } @Override public void readAdditionalSaveData(CompoundTag tag) { super.readAdditionalSaveData(tag); this.setVariant((Salmon.Variant)tag.read("type", Salmon.Variant.CODEC).orElse(Salmon.Variant.DEFAULT)); } @Override public void saveToBucketTag(ItemStack stack) { Bucketable.saveDefaultDataToBucketTag(this, stack); stack.copyFrom(DataComponents.SALMON_SIZE, this); } private void setVariant(Salmon.Variant variant) { this.entityData.set(DATA_TYPE, variant.id); } public Salmon.Variant getVariant() { return (Salmon.Variant)Salmon.Variant.BY_ID.apply(this.entityData.get(DATA_TYPE)); } @Nullable @Override public T get(DataComponentType component) { return component == DataComponents.SALMON_SIZE ? castComponentValue((DataComponentType)component, this.getVariant()) : super.get(component); } @Override protected void applyImplicitComponents(DataComponentGetter componentGetter) { this.applyImplicitComponentIfPresent(componentGetter, DataComponents.SALMON_SIZE); super.applyImplicitComponents(componentGetter); } @Override protected boolean applyImplicitComponent(DataComponentType component, T value) { if (component == DataComponents.SALMON_SIZE) { this.setVariant(castComponentValue(DataComponents.SALMON_SIZE, value)); return true; } else { return super.applyImplicitComponent(component, value); } } @Nullable @Override public SpawnGroupData finalizeSpawn( ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData ) { WeightedList.Builder builder = WeightedList.builder(); builder.add(Salmon.Variant.SMALL, 30); builder.add(Salmon.Variant.MEDIUM, 50); builder.add(Salmon.Variant.LARGE, 15); builder.build().getRandom(this.random).ifPresent(this::setVariant); return super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData); } public float getSalmonScale() { return this.getVariant().boundingBoxScale; } @Override protected EntityDimensions getDefaultDimensions(Pose pose) { return super.getDefaultDimensions(pose).scale(this.getSalmonScale()); } public static enum Variant implements StringRepresentable { SMALL("small", 0, 0.5F), MEDIUM("medium", 1, 1.0F), LARGE("large", 2, 1.5F); public static final Salmon.Variant DEFAULT = MEDIUM; public static final StringRepresentable.EnumCodec CODEC = StringRepresentable.fromEnum(Salmon.Variant::values); static final IntFunction BY_ID = ByIdMap.continuous(Salmon.Variant::id, values(), OutOfBoundsStrategy.CLAMP); public static final StreamCodec STREAM_CODEC = ByteBufCodecs.idMapper(BY_ID, Salmon.Variant::id); private final String name; final int id; final float boundingBoxScale; private Variant(final String name, final int id, final float boundingBoxScale) { this.name = name; this.id = id; this.boundingBoxScale = boundingBoxScale; } @Override public String getSerializedName() { return this.name; } int id() { return this.id; } } }