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

376 lines
15 KiB
Java

package net.minecraft.world.entity.animal;
import com.mojang.serialization.Codec;
import io.netty.buffer.ByteBuf;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.IntFunction;
import net.minecraft.ChatFormatting;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
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.chat.Component;
import net.minecraft.network.chat.MutableComponent;
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.tags.BiomeTags;
import net.minecraft.tags.FluidTags;
import net.minecraft.util.ByIdMap;
import net.minecraft.util.RandomSource;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EntitySpawnReason;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.SpawnGroupData;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.component.TooltipProvider;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.block.Blocks;
import org.jetbrains.annotations.Nullable;
public class TropicalFish extends AbstractSchoolingFish {
public static final TropicalFish.Variant DEFAULT_VARIANT = new TropicalFish.Variant(TropicalFish.Pattern.KOB, DyeColor.WHITE, DyeColor.WHITE);
private static final EntityDataAccessor<Integer> DATA_ID_TYPE_VARIANT = SynchedEntityData.defineId(TropicalFish.class, EntityDataSerializers.INT);
public static final List<TropicalFish.Variant> COMMON_VARIANTS = List.of(
new TropicalFish.Variant(TropicalFish.Pattern.STRIPEY, DyeColor.ORANGE, DyeColor.GRAY),
new TropicalFish.Variant(TropicalFish.Pattern.FLOPPER, DyeColor.GRAY, DyeColor.GRAY),
new TropicalFish.Variant(TropicalFish.Pattern.FLOPPER, DyeColor.GRAY, DyeColor.BLUE),
new TropicalFish.Variant(TropicalFish.Pattern.CLAYFISH, DyeColor.WHITE, DyeColor.GRAY),
new TropicalFish.Variant(TropicalFish.Pattern.SUNSTREAK, DyeColor.BLUE, DyeColor.GRAY),
new TropicalFish.Variant(TropicalFish.Pattern.KOB, DyeColor.ORANGE, DyeColor.WHITE),
new TropicalFish.Variant(TropicalFish.Pattern.SPOTTY, DyeColor.PINK, DyeColor.LIGHT_BLUE),
new TropicalFish.Variant(TropicalFish.Pattern.BLOCKFISH, DyeColor.PURPLE, DyeColor.YELLOW),
new TropicalFish.Variant(TropicalFish.Pattern.CLAYFISH, DyeColor.WHITE, DyeColor.RED),
new TropicalFish.Variant(TropicalFish.Pattern.SPOTTY, DyeColor.WHITE, DyeColor.YELLOW),
new TropicalFish.Variant(TropicalFish.Pattern.GLITTER, DyeColor.WHITE, DyeColor.GRAY),
new TropicalFish.Variant(TropicalFish.Pattern.CLAYFISH, DyeColor.WHITE, DyeColor.ORANGE),
new TropicalFish.Variant(TropicalFish.Pattern.DASHER, DyeColor.CYAN, DyeColor.PINK),
new TropicalFish.Variant(TropicalFish.Pattern.BRINELY, DyeColor.LIME, DyeColor.LIGHT_BLUE),
new TropicalFish.Variant(TropicalFish.Pattern.BETTY, DyeColor.RED, DyeColor.WHITE),
new TropicalFish.Variant(TropicalFish.Pattern.SNOOPER, DyeColor.GRAY, DyeColor.RED),
new TropicalFish.Variant(TropicalFish.Pattern.BLOCKFISH, DyeColor.RED, DyeColor.WHITE),
new TropicalFish.Variant(TropicalFish.Pattern.FLOPPER, DyeColor.WHITE, DyeColor.YELLOW),
new TropicalFish.Variant(TropicalFish.Pattern.KOB, DyeColor.RED, DyeColor.WHITE),
new TropicalFish.Variant(TropicalFish.Pattern.SUNSTREAK, DyeColor.GRAY, DyeColor.WHITE),
new TropicalFish.Variant(TropicalFish.Pattern.DASHER, DyeColor.CYAN, DyeColor.YELLOW),
new TropicalFish.Variant(TropicalFish.Pattern.FLOPPER, DyeColor.YELLOW, DyeColor.YELLOW)
);
private boolean isSchool = true;
public TropicalFish(EntityType<? extends TropicalFish> entityType, Level level) {
super(entityType, level);
}
public static String getPredefinedName(int variantId) {
return "entity.minecraft.tropical_fish.predefined." + variantId;
}
static int packVariant(TropicalFish.Pattern pattern, DyeColor baseColor, DyeColor patternColor) {
return pattern.getPackedId() & 65535 | (baseColor.getId() & 0xFF) << 16 | (patternColor.getId() & 0xFF) << 24;
}
public static DyeColor getBaseColor(int variantId) {
return DyeColor.byId(variantId >> 16 & 0xFF);
}
public static DyeColor getPatternColor(int variantId) {
return DyeColor.byId(variantId >> 24 & 0xFF);
}
public static TropicalFish.Pattern getPattern(int variantId) {
return TropicalFish.Pattern.byId(variantId & 65535);
}
@Override
protected void defineSynchedData(Builder builder) {
super.defineSynchedData(builder);
builder.define(DATA_ID_TYPE_VARIANT, DEFAULT_VARIANT.getPackedId());
}
@Override
public void addAdditionalSaveData(CompoundTag tag) {
super.addAdditionalSaveData(tag);
tag.store("Variant", TropicalFish.Variant.CODEC, new TropicalFish.Variant(this.getPackedVariant()));
}
@Override
public void readAdditionalSaveData(CompoundTag tag) {
super.readAdditionalSaveData(tag);
TropicalFish.Variant variant = (TropicalFish.Variant)tag.read("Variant", TropicalFish.Variant.CODEC).orElse(DEFAULT_VARIANT);
this.setPackedVariant(variant.getPackedId());
}
private void setPackedVariant(int packedVariant) {
this.entityData.set(DATA_ID_TYPE_VARIANT, packedVariant);
}
@Override
public boolean isMaxGroupSizeReached(int size) {
return !this.isSchool;
}
private int getPackedVariant() {
return this.entityData.get(DATA_ID_TYPE_VARIANT);
}
public DyeColor getBaseColor() {
return getBaseColor(this.getPackedVariant());
}
public DyeColor getPatternColor() {
return getPatternColor(this.getPackedVariant());
}
public TropicalFish.Pattern getPattern() {
return getPattern(this.getPackedVariant());
}
private void setPattern(TropicalFish.Pattern pattern) {
int i = this.getPackedVariant();
DyeColor dyeColor = getBaseColor(i);
DyeColor dyeColor2 = getPatternColor(i);
this.setPackedVariant(packVariant(pattern, dyeColor, dyeColor2));
}
private void setBaseColor(DyeColor baseColor) {
int i = this.getPackedVariant();
TropicalFish.Pattern pattern = getPattern(i);
DyeColor dyeColor = getPatternColor(i);
this.setPackedVariant(packVariant(pattern, baseColor, dyeColor));
}
private void setPatternColor(DyeColor patternColor) {
int i = this.getPackedVariant();
TropicalFish.Pattern pattern = getPattern(i);
DyeColor dyeColor = getBaseColor(i);
this.setPackedVariant(packVariant(pattern, dyeColor, patternColor));
}
@Nullable
@Override
public <T> T get(DataComponentType<? extends T> component) {
if (component == DataComponents.TROPICAL_FISH_PATTERN) {
return castComponentValue((DataComponentType<T>)component, this.getPattern());
} else if (component == DataComponents.TROPICAL_FISH_BASE_COLOR) {
return castComponentValue((DataComponentType<T>)component, this.getBaseColor());
} else {
return component == DataComponents.TROPICAL_FISH_PATTERN_COLOR
? castComponentValue((DataComponentType<T>)component, this.getPatternColor())
: super.get(component);
}
}
@Override
protected void applyImplicitComponents(DataComponentGetter componentGetter) {
this.applyImplicitComponentIfPresent(componentGetter, DataComponents.TROPICAL_FISH_PATTERN);
this.applyImplicitComponentIfPresent(componentGetter, DataComponents.TROPICAL_FISH_BASE_COLOR);
this.applyImplicitComponentIfPresent(componentGetter, DataComponents.TROPICAL_FISH_PATTERN_COLOR);
super.applyImplicitComponents(componentGetter);
}
@Override
protected <T> boolean applyImplicitComponent(DataComponentType<T> component, T value) {
if (component == DataComponents.TROPICAL_FISH_PATTERN) {
this.setPattern(castComponentValue(DataComponents.TROPICAL_FISH_PATTERN, value));
return true;
} else if (component == DataComponents.TROPICAL_FISH_BASE_COLOR) {
this.setBaseColor(castComponentValue(DataComponents.TROPICAL_FISH_BASE_COLOR, value));
return true;
} else if (component == DataComponents.TROPICAL_FISH_PATTERN_COLOR) {
this.setPatternColor(castComponentValue(DataComponents.TROPICAL_FISH_PATTERN_COLOR, value));
return true;
} else {
return super.applyImplicitComponent(component, value);
}
}
@Override
public void saveToBucketTag(ItemStack stack) {
super.saveToBucketTag(stack);
stack.copyFrom(DataComponents.TROPICAL_FISH_PATTERN, this);
stack.copyFrom(DataComponents.TROPICAL_FISH_BASE_COLOR, this);
stack.copyFrom(DataComponents.TROPICAL_FISH_PATTERN_COLOR, this);
}
@Override
public ItemStack getBucketItemStack() {
return new ItemStack(Items.TROPICAL_FISH_BUCKET);
}
@Override
protected SoundEvent getAmbientSound() {
return SoundEvents.TROPICAL_FISH_AMBIENT;
}
@Override
protected SoundEvent getDeathSound() {
return SoundEvents.TROPICAL_FISH_DEATH;
}
@Override
protected SoundEvent getHurtSound(DamageSource damageSource) {
return SoundEvents.TROPICAL_FISH_HURT;
}
@Override
protected SoundEvent getFlopSound() {
return SoundEvents.TROPICAL_FISH_FLOP;
}
@Nullable
@Override
public SpawnGroupData finalizeSpawn(
ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData
) {
spawnGroupData = super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData);
RandomSource randomSource = level.getRandom();
TropicalFish.Variant variant;
if (spawnGroupData instanceof TropicalFish.TropicalFishGroupData tropicalFishGroupData) {
variant = tropicalFishGroupData.variant;
} else if (randomSource.nextFloat() < 0.9) {
variant = Util.getRandom(COMMON_VARIANTS, randomSource);
spawnGroupData = new TropicalFish.TropicalFishGroupData(this, variant);
} else {
this.isSchool = false;
TropicalFish.Pattern[] patterns = TropicalFish.Pattern.values();
DyeColor[] dyeColors = DyeColor.values();
TropicalFish.Pattern pattern = Util.getRandom(patterns, randomSource);
DyeColor dyeColor = Util.getRandom(dyeColors, randomSource);
DyeColor dyeColor2 = Util.getRandom(dyeColors, randomSource);
variant = new TropicalFish.Variant(pattern, dyeColor, dyeColor2);
}
this.setPackedVariant(variant.getPackedId());
return spawnGroupData;
}
public static boolean checkTropicalFishSpawnRules(
EntityType<TropicalFish> entityType, LevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random
) {
return level.getFluidState(pos.below()).is(FluidTags.WATER)
&& level.getBlockState(pos.above()).is(Blocks.WATER)
&& (
level.getBiome(pos).is(BiomeTags.ALLOWS_TROPICAL_FISH_SPAWNS_AT_ANY_HEIGHT)
|| WaterAnimal.checkSurfaceWaterAnimalSpawnRules(entityType, level, spawnReason, pos, random)
);
}
public static enum Base {
SMALL(0),
LARGE(1);
final int id;
private Base(final int id) {
this.id = id;
}
}
public static enum Pattern implements StringRepresentable, TooltipProvider {
KOB("kob", TropicalFish.Base.SMALL, 0),
SUNSTREAK("sunstreak", TropicalFish.Base.SMALL, 1),
SNOOPER("snooper", TropicalFish.Base.SMALL, 2),
DASHER("dasher", TropicalFish.Base.SMALL, 3),
BRINELY("brinely", TropicalFish.Base.SMALL, 4),
SPOTTY("spotty", TropicalFish.Base.SMALL, 5),
FLOPPER("flopper", TropicalFish.Base.LARGE, 0),
STRIPEY("stripey", TropicalFish.Base.LARGE, 1),
GLITTER("glitter", TropicalFish.Base.LARGE, 2),
BLOCKFISH("blockfish", TropicalFish.Base.LARGE, 3),
BETTY("betty", TropicalFish.Base.LARGE, 4),
CLAYFISH("clayfish", TropicalFish.Base.LARGE, 5);
public static final Codec<TropicalFish.Pattern> CODEC = StringRepresentable.fromEnum(TropicalFish.Pattern::values);
private static final IntFunction<TropicalFish.Pattern> BY_ID = ByIdMap.sparse(TropicalFish.Pattern::getPackedId, values(), KOB);
public static final StreamCodec<ByteBuf, TropicalFish.Pattern> STREAM_CODEC = ByteBufCodecs.idMapper(BY_ID, TropicalFish.Pattern::getPackedId);
private final String name;
private final Component displayName;
private final TropicalFish.Base base;
private final int packedId;
private Pattern(final String name, final TropicalFish.Base base, final int id) {
this.name = name;
this.base = base;
this.packedId = base.id | id << 8;
this.displayName = Component.translatable("entity.minecraft.tropical_fish.type." + this.name);
}
public static TropicalFish.Pattern byId(int packedId) {
return (TropicalFish.Pattern)BY_ID.apply(packedId);
}
public TropicalFish.Base base() {
return this.base;
}
public int getPackedId() {
return this.packedId;
}
@Override
public String getSerializedName() {
return this.name;
}
public Component displayName() {
return this.displayName;
}
@Override
public void addToTooltip(Item.TooltipContext context, Consumer<Component> tooltipAdder, TooltipFlag flag, DataComponentGetter componentGetter) {
DyeColor dyeColor = componentGetter.getOrDefault(DataComponents.TROPICAL_FISH_BASE_COLOR, TropicalFish.DEFAULT_VARIANT.baseColor());
DyeColor dyeColor2 = componentGetter.getOrDefault(DataComponents.TROPICAL_FISH_PATTERN_COLOR, TropicalFish.DEFAULT_VARIANT.patternColor());
ChatFormatting[] chatFormattings = new ChatFormatting[]{ChatFormatting.ITALIC, ChatFormatting.GRAY};
int i = TropicalFish.COMMON_VARIANTS.indexOf(new TropicalFish.Variant(this, dyeColor, dyeColor2));
if (i != -1) {
tooltipAdder.accept(Component.translatable(TropicalFish.getPredefinedName(i)).withStyle(chatFormattings));
} else {
tooltipAdder.accept(this.displayName.plainCopy().withStyle(chatFormattings));
MutableComponent mutableComponent = Component.translatable("color.minecraft." + dyeColor.getName());
if (dyeColor != dyeColor2) {
mutableComponent.append(", ").append(Component.translatable("color.minecraft." + dyeColor2.getName()));
}
mutableComponent.withStyle(chatFormattings);
tooltipAdder.accept(mutableComponent);
}
}
}
static class TropicalFishGroupData extends AbstractSchoolingFish.SchoolSpawnGroupData {
final TropicalFish.Variant variant;
TropicalFishGroupData(TropicalFish leader, TropicalFish.Variant variant) {
super(leader);
this.variant = variant;
}
}
public record Variant(TropicalFish.Pattern pattern, DyeColor baseColor, DyeColor patternColor) {
public static final Codec<TropicalFish.Variant> CODEC = Codec.INT.xmap(TropicalFish.Variant::new, TropicalFish.Variant::getPackedId);
public Variant(int id) {
this(TropicalFish.getPattern(id), TropicalFish.getBaseColor(id), TropicalFish.getPatternColor(id));
}
public int getPackedId() {
return TropicalFish.packVariant(this.pattern, this.baseColor, this.patternColor);
}
}
}