429 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			429 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.world.level.biome;
 | |
| 
 | |
| import com.google.common.collect.ImmutableList;
 | |
| import com.mojang.serialization.Codec;
 | |
| import com.mojang.serialization.MapCodec;
 | |
| import com.mojang.serialization.codecs.RecordCodecBuilder;
 | |
| import it.unimi.dsi.fastutil.longs.Long2FloatLinkedOpenHashMap;
 | |
| import java.util.Optional;
 | |
| import net.minecraft.Util;
 | |
| import net.minecraft.core.BlockPos;
 | |
| import net.minecraft.core.Holder;
 | |
| import net.minecraft.core.HolderSet;
 | |
| import net.minecraft.core.RegistryCodecs;
 | |
| import net.minecraft.core.registries.Registries;
 | |
| import net.minecraft.resources.RegistryFileCodec;
 | |
| import net.minecraft.sounds.Music;
 | |
| import net.minecraft.sounds.SoundEvent;
 | |
| import net.minecraft.util.Mth;
 | |
| import net.minecraft.util.StringRepresentable;
 | |
| import net.minecraft.util.random.WeightedList;
 | |
| import net.minecraft.world.level.DryFoliageColor;
 | |
| import net.minecraft.world.level.FoliageColor;
 | |
| import net.minecraft.world.level.GrassColor;
 | |
| import net.minecraft.world.level.LevelReader;
 | |
| import net.minecraft.world.level.LightLayer;
 | |
| import net.minecraft.world.level.block.Blocks;
 | |
| import net.minecraft.world.level.block.LiquidBlock;
 | |
| import net.minecraft.world.level.block.state.BlockState;
 | |
| import net.minecraft.world.level.levelgen.LegacyRandomSource;
 | |
| import net.minecraft.world.level.levelgen.WorldgenRandom;
 | |
| import net.minecraft.world.level.levelgen.synth.PerlinSimplexNoise;
 | |
| import net.minecraft.world.level.material.FluidState;
 | |
| import net.minecraft.world.level.material.Fluids;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| 
 | |
| public final class Biome {
 | |
| 	public static final Codec<Biome> DIRECT_CODEC = RecordCodecBuilder.create(
 | |
| 		instance -> instance.group(
 | |
| 				Biome.ClimateSettings.CODEC.forGetter(biome -> biome.climateSettings),
 | |
| 				BiomeSpecialEffects.CODEC.fieldOf("effects").forGetter(biome -> biome.specialEffects),
 | |
| 				BiomeGenerationSettings.CODEC.forGetter(biome -> biome.generationSettings),
 | |
| 				MobSpawnSettings.CODEC.forGetter(biome -> biome.mobSettings)
 | |
| 			)
 | |
| 			.apply(instance, Biome::new)
 | |
| 	);
 | |
| 	public static final Codec<Biome> NETWORK_CODEC = RecordCodecBuilder.create(
 | |
| 		instance -> instance.group(
 | |
| 				Biome.ClimateSettings.CODEC.forGetter(biome -> biome.climateSettings),
 | |
| 				BiomeSpecialEffects.CODEC.fieldOf("effects").forGetter(biome -> biome.specialEffects)
 | |
| 			)
 | |
| 			.apply(
 | |
| 				instance, (climateSettings, biomeSpecialEffects) -> new Biome(climateSettings, biomeSpecialEffects, BiomeGenerationSettings.EMPTY, MobSpawnSettings.EMPTY)
 | |
| 			)
 | |
| 	);
 | |
| 	public static final Codec<Holder<Biome>> CODEC = RegistryFileCodec.create(Registries.BIOME, DIRECT_CODEC);
 | |
| 	public static final Codec<HolderSet<Biome>> LIST_CODEC = RegistryCodecs.homogeneousList(Registries.BIOME, DIRECT_CODEC);
 | |
| 	private static final PerlinSimplexNoise TEMPERATURE_NOISE = new PerlinSimplexNoise(new WorldgenRandom(new LegacyRandomSource(1234L)), ImmutableList.of(0));
 | |
| 	static final PerlinSimplexNoise FROZEN_TEMPERATURE_NOISE = new PerlinSimplexNoise(
 | |
| 		new WorldgenRandom(new LegacyRandomSource(3456L)), ImmutableList.of(-2, -1, 0)
 | |
| 	);
 | |
| 	@Deprecated(
 | |
| 		forRemoval = true
 | |
| 	)
 | |
| 	public static final PerlinSimplexNoise BIOME_INFO_NOISE = new PerlinSimplexNoise(new WorldgenRandom(new LegacyRandomSource(2345L)), ImmutableList.of(0));
 | |
| 	private static final int TEMPERATURE_CACHE_SIZE = 1024;
 | |
| 	private final Biome.ClimateSettings climateSettings;
 | |
| 	private final BiomeGenerationSettings generationSettings;
 | |
| 	private final MobSpawnSettings mobSettings;
 | |
| 	private final BiomeSpecialEffects specialEffects;
 | |
| 	private final ThreadLocal<Long2FloatLinkedOpenHashMap> temperatureCache = ThreadLocal.withInitial(() -> Util.make(() -> {
 | |
| 		Long2FloatLinkedOpenHashMap long2FloatLinkedOpenHashMap = new Long2FloatLinkedOpenHashMap(1024, 0.25F) {
 | |
| 			@Override
 | |
| 			protected void rehash(int i) {
 | |
| 			}
 | |
| 		};
 | |
| 		long2FloatLinkedOpenHashMap.defaultReturnValue(Float.NaN);
 | |
| 		return long2FloatLinkedOpenHashMap;
 | |
| 	}));
 | |
| 
 | |
| 	Biome(Biome.ClimateSettings climateSettings, BiomeSpecialEffects specialEffects, BiomeGenerationSettings generationSettings, MobSpawnSettings mobSettings) {
 | |
| 		this.climateSettings = climateSettings;
 | |
| 		this.generationSettings = generationSettings;
 | |
| 		this.mobSettings = mobSettings;
 | |
| 		this.specialEffects = specialEffects;
 | |
| 	}
 | |
| 
 | |
| 	public int getSkyColor() {
 | |
| 		return this.specialEffects.getSkyColor();
 | |
| 	}
 | |
| 
 | |
| 	public MobSpawnSettings getMobSettings() {
 | |
| 		return this.mobSettings;
 | |
| 	}
 | |
| 
 | |
| 	public boolean hasPrecipitation() {
 | |
| 		return this.climateSettings.hasPrecipitation();
 | |
| 	}
 | |
| 
 | |
| 	public Biome.Precipitation getPrecipitationAt(BlockPos pos, int seaLevel) {
 | |
| 		if (!this.hasPrecipitation()) {
 | |
| 			return Biome.Precipitation.NONE;
 | |
| 		} else {
 | |
| 			return this.coldEnoughToSnow(pos, seaLevel) ? Biome.Precipitation.SNOW : Biome.Precipitation.RAIN;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private float getHeightAdjustedTemperature(BlockPos pos, int seaLevel) {
 | |
| 		float f = this.climateSettings.temperatureModifier.modifyTemperature(pos, this.getBaseTemperature());
 | |
| 		int i = seaLevel + 17;
 | |
| 		if (pos.getY() > i) {
 | |
| 			float g = (float)(TEMPERATURE_NOISE.getValue(pos.getX() / 8.0F, pos.getZ() / 8.0F, false) * 8.0);
 | |
| 			return f - (g + pos.getY() - i) * 0.05F / 40.0F;
 | |
| 		} else {
 | |
| 			return f;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Deprecated
 | |
| 	private float getTemperature(BlockPos pos, int seaLevel) {
 | |
| 		long l = pos.asLong();
 | |
| 		Long2FloatLinkedOpenHashMap long2FloatLinkedOpenHashMap = (Long2FloatLinkedOpenHashMap)this.temperatureCache.get();
 | |
| 		float f = long2FloatLinkedOpenHashMap.get(l);
 | |
| 		if (!Float.isNaN(f)) {
 | |
| 			return f;
 | |
| 		} else {
 | |
| 			float g = this.getHeightAdjustedTemperature(pos, seaLevel);
 | |
| 			if (long2FloatLinkedOpenHashMap.size() == 1024) {
 | |
| 				long2FloatLinkedOpenHashMap.removeFirstFloat();
 | |
| 			}
 | |
| 
 | |
| 			long2FloatLinkedOpenHashMap.put(l, g);
 | |
| 			return g;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public boolean shouldFreeze(LevelReader level, BlockPos pos) {
 | |
| 		return this.shouldFreeze(level, pos, true);
 | |
| 	}
 | |
| 
 | |
| 	public boolean shouldFreeze(LevelReader level, BlockPos water, boolean mustBeAtEdge) {
 | |
| 		if (this.warmEnoughToRain(water, level.getSeaLevel())) {
 | |
| 			return false;
 | |
| 		} else {
 | |
| 			if (level.isInsideBuildHeight(water.getY()) && level.getBrightness(LightLayer.BLOCK, water) < 10) {
 | |
| 				BlockState blockState = level.getBlockState(water);
 | |
| 				FluidState fluidState = level.getFluidState(water);
 | |
| 				if (fluidState.getType() == Fluids.WATER && blockState.getBlock() instanceof LiquidBlock) {
 | |
| 					if (!mustBeAtEdge) {
 | |
| 						return true;
 | |
| 					}
 | |
| 
 | |
| 					boolean bl = level.isWaterAt(water.west()) && level.isWaterAt(water.east()) && level.isWaterAt(water.north()) && level.isWaterAt(water.south());
 | |
| 					if (!bl) {
 | |
| 						return true;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			return false;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public boolean coldEnoughToSnow(BlockPos pos, int seaLevel) {
 | |
| 		return !this.warmEnoughToRain(pos, seaLevel);
 | |
| 	}
 | |
| 
 | |
| 	public boolean warmEnoughToRain(BlockPos pos, int seaLevel) {
 | |
| 		return this.getTemperature(pos, seaLevel) >= 0.15F;
 | |
| 	}
 | |
| 
 | |
| 	public boolean shouldMeltFrozenOceanIcebergSlightly(BlockPos pos, int seaLevel) {
 | |
| 		return this.getTemperature(pos, seaLevel) > 0.1F;
 | |
| 	}
 | |
| 
 | |
| 	public boolean shouldSnow(LevelReader level, BlockPos pos) {
 | |
| 		if (this.warmEnoughToRain(pos, level.getSeaLevel())) {
 | |
| 			return false;
 | |
| 		} else {
 | |
| 			if (level.isInsideBuildHeight(pos.getY()) && level.getBrightness(LightLayer.BLOCK, pos) < 10) {
 | |
| 				BlockState blockState = level.getBlockState(pos);
 | |
| 				if ((blockState.isAir() || blockState.is(Blocks.SNOW)) && Blocks.SNOW.defaultBlockState().canSurvive(level, pos)) {
 | |
| 					return true;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			return false;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public BiomeGenerationSettings getGenerationSettings() {
 | |
| 		return this.generationSettings;
 | |
| 	}
 | |
| 
 | |
| 	public int getFogColor() {
 | |
| 		return this.specialEffects.getFogColor();
 | |
| 	}
 | |
| 
 | |
| 	public int getGrassColor(double posX, double posZ) {
 | |
| 		int i = this.getBaseGrassColor();
 | |
| 		return this.specialEffects.getGrassColorModifier().modifyColor(posX, posZ, i);
 | |
| 	}
 | |
| 
 | |
| 	private int getBaseGrassColor() {
 | |
| 		Optional<Integer> optional = this.specialEffects.getGrassColorOverride();
 | |
| 		return optional.isPresent() ? (Integer)optional.get() : this.getGrassColorFromTexture();
 | |
| 	}
 | |
| 
 | |
| 	private int getGrassColorFromTexture() {
 | |
| 		double d = Mth.clamp(this.climateSettings.temperature, 0.0F, 1.0F);
 | |
| 		double e = Mth.clamp(this.climateSettings.downfall, 0.0F, 1.0F);
 | |
| 		return GrassColor.get(d, e);
 | |
| 	}
 | |
| 
 | |
| 	public int getFoliageColor() {
 | |
| 		return (Integer)this.specialEffects.getFoliageColorOverride().orElseGet(this::getFoliageColorFromTexture);
 | |
| 	}
 | |
| 
 | |
| 	private int getFoliageColorFromTexture() {
 | |
| 		double d = Mth.clamp(this.climateSettings.temperature, 0.0F, 1.0F);
 | |
| 		double e = Mth.clamp(this.climateSettings.downfall, 0.0F, 1.0F);
 | |
| 		return FoliageColor.get(d, e);
 | |
| 	}
 | |
| 
 | |
| 	public int getDryFoliageColor() {
 | |
| 		return (Integer)this.specialEffects.getDryFoliageColorOverride().orElseGet(this::getDryFoliageColorFromTexture);
 | |
| 	}
 | |
| 
 | |
| 	private int getDryFoliageColorFromTexture() {
 | |
| 		double d = Mth.clamp(this.climateSettings.temperature, 0.0F, 1.0F);
 | |
| 		double e = Mth.clamp(this.climateSettings.downfall, 0.0F, 1.0F);
 | |
| 		return DryFoliageColor.get(d, e);
 | |
| 	}
 | |
| 
 | |
| 	public float getBaseTemperature() {
 | |
| 		return this.climateSettings.temperature;
 | |
| 	}
 | |
| 
 | |
| 	public BiomeSpecialEffects getSpecialEffects() {
 | |
| 		return this.specialEffects;
 | |
| 	}
 | |
| 
 | |
| 	public int getWaterColor() {
 | |
| 		return this.specialEffects.getWaterColor();
 | |
| 	}
 | |
| 
 | |
| 	public int getWaterFogColor() {
 | |
| 		return this.specialEffects.getWaterFogColor();
 | |
| 	}
 | |
| 
 | |
| 	public Optional<AmbientParticleSettings> getAmbientParticle() {
 | |
| 		return this.specialEffects.getAmbientParticleSettings();
 | |
| 	}
 | |
| 
 | |
| 	public Optional<Holder<SoundEvent>> getAmbientLoop() {
 | |
| 		return this.specialEffects.getAmbientLoopSoundEvent();
 | |
| 	}
 | |
| 
 | |
| 	public Optional<AmbientMoodSettings> getAmbientMood() {
 | |
| 		return this.specialEffects.getAmbientMoodSettings();
 | |
| 	}
 | |
| 
 | |
| 	public Optional<AmbientAdditionsSettings> getAmbientAdditions() {
 | |
| 		return this.specialEffects.getAmbientAdditionsSettings();
 | |
| 	}
 | |
| 
 | |
| 	public Optional<WeightedList<Music>> getBackgroundMusic() {
 | |
| 		return this.specialEffects.getBackgroundMusic();
 | |
| 	}
 | |
| 
 | |
| 	public float getBackgroundMusicVolume() {
 | |
| 		return this.specialEffects.getBackgroundMusicVolume();
 | |
| 	}
 | |
| 
 | |
| 	public static class BiomeBuilder {
 | |
| 		private boolean hasPrecipitation = true;
 | |
| 		@Nullable
 | |
| 		private Float temperature;
 | |
| 		private Biome.TemperatureModifier temperatureModifier = Biome.TemperatureModifier.NONE;
 | |
| 		@Nullable
 | |
| 		private Float downfall;
 | |
| 		@Nullable
 | |
| 		private BiomeSpecialEffects specialEffects;
 | |
| 		@Nullable
 | |
| 		private MobSpawnSettings mobSpawnSettings;
 | |
| 		@Nullable
 | |
| 		private BiomeGenerationSettings generationSettings;
 | |
| 
 | |
| 		public Biome.BiomeBuilder hasPrecipitation(boolean hasPrecipitation) {
 | |
| 			this.hasPrecipitation = hasPrecipitation;
 | |
| 			return this;
 | |
| 		}
 | |
| 
 | |
| 		public Biome.BiomeBuilder temperature(float temperature) {
 | |
| 			this.temperature = temperature;
 | |
| 			return this;
 | |
| 		}
 | |
| 
 | |
| 		public Biome.BiomeBuilder downfall(float downfall) {
 | |
| 			this.downfall = downfall;
 | |
| 			return this;
 | |
| 		}
 | |
| 
 | |
| 		public Biome.BiomeBuilder specialEffects(BiomeSpecialEffects effects) {
 | |
| 			this.specialEffects = effects;
 | |
| 			return this;
 | |
| 		}
 | |
| 
 | |
| 		public Biome.BiomeBuilder mobSpawnSettings(MobSpawnSettings mobSpawnSettings) {
 | |
| 			this.mobSpawnSettings = mobSpawnSettings;
 | |
| 			return this;
 | |
| 		}
 | |
| 
 | |
| 		public Biome.BiomeBuilder generationSettings(BiomeGenerationSettings generationSettings) {
 | |
| 			this.generationSettings = generationSettings;
 | |
| 			return this;
 | |
| 		}
 | |
| 
 | |
| 		public Biome.BiomeBuilder temperatureAdjustment(Biome.TemperatureModifier temperatureSettings) {
 | |
| 			this.temperatureModifier = temperatureSettings;
 | |
| 			return this;
 | |
| 		}
 | |
| 
 | |
| 		public Biome build() {
 | |
| 			if (this.temperature != null && this.downfall != null && this.specialEffects != null && this.mobSpawnSettings != null && this.generationSettings != null) {
 | |
| 				return new Biome(
 | |
| 					new Biome.ClimateSettings(this.hasPrecipitation, this.temperature, this.temperatureModifier, this.downfall),
 | |
| 					this.specialEffects,
 | |
| 					this.generationSettings,
 | |
| 					this.mobSpawnSettings
 | |
| 				);
 | |
| 			} else {
 | |
| 				throw new IllegalStateException("You are missing parameters to build a proper biome\n" + this);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		public String toString() {
 | |
| 			return "BiomeBuilder{\nhasPrecipitation="
 | |
| 				+ this.hasPrecipitation
 | |
| 				+ ",\ntemperature="
 | |
| 				+ this.temperature
 | |
| 				+ ",\ntemperatureModifier="
 | |
| 				+ this.temperatureModifier
 | |
| 				+ ",\ndownfall="
 | |
| 				+ this.downfall
 | |
| 				+ ",\nspecialEffects="
 | |
| 				+ this.specialEffects
 | |
| 				+ ",\nmobSpawnSettings="
 | |
| 				+ this.mobSpawnSettings
 | |
| 				+ ",\ngenerationSettings="
 | |
| 				+ this.generationSettings
 | |
| 				+ ",\n}";
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	record ClimateSettings(boolean hasPrecipitation, float temperature, Biome.TemperatureModifier temperatureModifier, float downfall) {
 | |
| 		public static final MapCodec<Biome.ClimateSettings> CODEC = RecordCodecBuilder.mapCodec(
 | |
| 			instance -> instance.group(
 | |
| 					Codec.BOOL.fieldOf("has_precipitation").forGetter(climateSettings -> climateSettings.hasPrecipitation),
 | |
| 					Codec.FLOAT.fieldOf("temperature").forGetter(climateSettings -> climateSettings.temperature),
 | |
| 					Biome.TemperatureModifier.CODEC
 | |
| 						.optionalFieldOf("temperature_modifier", Biome.TemperatureModifier.NONE)
 | |
| 						.forGetter(climateSettings -> climateSettings.temperatureModifier),
 | |
| 					Codec.FLOAT.fieldOf("downfall").forGetter(climateSettings -> climateSettings.downfall)
 | |
| 				)
 | |
| 				.apply(instance, Biome.ClimateSettings::new)
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	public static enum Precipitation implements StringRepresentable {
 | |
| 		NONE("none"),
 | |
| 		RAIN("rain"),
 | |
| 		SNOW("snow");
 | |
| 
 | |
| 		public static final Codec<Biome.Precipitation> CODEC = StringRepresentable.fromEnum(Biome.Precipitation::values);
 | |
| 		private final String name;
 | |
| 
 | |
| 		private Precipitation(final String name) {
 | |
| 			this.name = name;
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public String getSerializedName() {
 | |
| 			return this.name;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public static enum TemperatureModifier implements StringRepresentable {
 | |
| 		NONE("none") {
 | |
| 			@Override
 | |
| 			public float modifyTemperature(BlockPos pos, float temperature) {
 | |
| 				return temperature;
 | |
| 			}
 | |
| 		},
 | |
| 		FROZEN("frozen") {
 | |
| 			@Override
 | |
| 			public float modifyTemperature(BlockPos pos, float temperature) {
 | |
| 				double d = Biome.FROZEN_TEMPERATURE_NOISE.getValue(pos.getX() * 0.05, pos.getZ() * 0.05, false) * 7.0;
 | |
| 				double e = Biome.BIOME_INFO_NOISE.getValue(pos.getX() * 0.2, pos.getZ() * 0.2, false);
 | |
| 				double f = d + e;
 | |
| 				if (f < 0.3) {
 | |
| 					double g = Biome.BIOME_INFO_NOISE.getValue(pos.getX() * 0.09, pos.getZ() * 0.09, false);
 | |
| 					if (g < 0.8) {
 | |
| 						return 0.2F;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				return temperature;
 | |
| 			}
 | |
| 		};
 | |
| 
 | |
| 		private final String name;
 | |
| 		public static final Codec<Biome.TemperatureModifier> CODEC = StringRepresentable.fromEnum(Biome.TemperatureModifier::values);
 | |
| 
 | |
| 		public abstract float modifyTemperature(BlockPos pos, float temperature);
 | |
| 
 | |
| 		TemperatureModifier(final String name) {
 | |
| 			this.name = name;
 | |
| 		}
 | |
| 
 | |
| 		public String getName() {
 | |
| 			return this.name;
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public String getSerializedName() {
 | |
| 			return this.name;
 | |
| 		}
 | |
| 	}
 | |
| }
 |