当前位置: 首页 > news >正文

我的世界1.20.1forge模组进阶开发——生物生成2

怎么才能在特定的生物群系或结构生成生物?我们来研究一下alexmob,看看它是怎么写的。

自定义生物群系生成规则

先研究源码以便更好的开发模组

AMMobSpawnBiomeModifier类

package com.github.alexthe666.alexsmobs.world;

import com.github.alexthe666.alexsmobs.AlexsMobs;
import com.mojang.serialization.Codec;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.biome.Biome;
import net.minecraftforge.common.world.BiomeModifier;
import net.minecraftforge.common.world.ModifiableBiomeInfo;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;

public class AMMobSpawnBiomeModifier implements BiomeModifier {
   
    private static final RegistryObject<Codec<? extends BiomeModifier>> SERIALIZER = RegistryObject.create(new ResourceLocation(AlexsMobs.MODID, "am_mob_spawns"), ForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS, AlexsMobs.MODID);

    public AMMobSpawnBiomeModifier() {
   
    }

    public void modify(Holder<Biome> biome, Phase phase, ModifiableBiomeInfo.BiomeInfo.Builder builder) {
   
        if (phase == Phase.ADD) {
   
            AMWorldRegistry.addBiomeSpawns(biome, builder);
        }
    }

    public Codec<? extends BiomeModifier> codec() {
   
        return (Codec)SERIALIZER.get();
    }

    public static Codec<AMMobSpawnBiomeModifier> makeCodec() {
   
        return Codec.unit(AMMobSpawnBiomeModifier::new);
    }
}


这段代码是 Minecraft Forge 模组 Alex’s Mobs 中用于动态修改生物群系(Biome)生成规则的实现。它的核心功能是向特定生物群系中添加自定义生物的生成规则。以下是对代码的详细解释:


1. 类定义与接口实现

public class AMMobSpawnBiomeModifier implements BiomeModifier {
   

• 类名 AMMobSpawnBiomeModifier 表明这是一个与生物生成(Mob Spawn)相关的生物群系修改器。
• 实现了 Forge 的 BiomeModifier 接口,允许在生物群系加载时动态修改其属性(如添加生物生成规则)。


2. 序列化器注册

private static final RegistryObject<Codec<? extends BiomeModifier>> SERIALIZER = 
    RegistryObject.create(
        new ResourceLocation(AlexsMobs.MODID, "am_mob_spawns"), 
        ForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS, 
        AlexsMobs.MODID
    );

作用: 注册一个序列化器(Codec),用于将配置数据与 AMMobSpawnBiomeModifier 实例相互转换。
关键参数:
ResourceLocation: 唯一标识符,格式为 <modid>:am_mob_spawns(例如 alexsmobs:am_mob_spawns)。
ForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS: 表明注册到 Forge 的生物群系修改器序列化器注册表中。
• 通过 RegistryObject 延迟获取序列化器,确保注册顺序正确。


3. 核心方法 modify

public void modify(Holder<Biome> biome, Phase phase, ModifiableBiomeInfo.BiomeInfo.Builder builder) {
   
    if (phase == Phase.ADD) {
   
        AMWorldRegistry.addBiomeSpawns(biome, builder);
    }
}

作用: 在生物群系加载时,根据指定的 Phase 阶段修改其属性。
参数:
biome: 当前正在处理的生物群系。
phase: 修改阶段(ADD 表示添加新内容,MODIFY 表示修改现有内容)。
builder: 用于构建生物群系信息的工具类。
逻辑:
• 仅在 Phase.ADD 阶段触发,调用 AMWorldRegistry.addBiomeSpawns 方法,将自定义生物的生成规则添加到该生物群系中。
AMWorldRegistry.addBiomeSpawns 的具体实现(未在此展示)可能涉及添加生物生成规则(如生物类型、生成权重、最小/最大数量等)。


4. 序列化相关方法

public Codec<? extends BiomeModifier> codec() {
   
    return (Codec)SERIALIZER.get();
}

public static Codec<AMMobSpawnBiomeModifier> makeCodec() {
   
    return Codec.unit(AMMobSpawnBiomeModifier::new);
}

codec():
• 返回该类使用的序列化器(Codec),即之前注册的 SERIALIZER
makeCodec():
• 创建一个简单的 Codec,直接通过构造函数 AMMobSpawnBiomeModifier::new 生成实例。
Codec.unit 表示该 Codec 不需要额外配置参数,直接返回一个默认实例。


5. 整体工作流程

  1. 注册序列化器:
    • 在 Forge 的注册表中声明 AMMobSpawnBiomeModifier 的序列化器,使游戏能识别并加载相关配置。

  2. 生物群系修改阶段:
    • 当游戏加载生物群系时,Forge 会调用 modify 方法。
    • 在 Phase.ADD 阶段,通过 AMWorldRegistry.addBiomeSpawns 向生物群系添加自定义生物生成规则。

  3. 数据驱动:
    • 通过 Codec 序列化/反序列化,可能支持通过 JSON 文件配置生物生成规则(如不同群系的生成参数)。


6. 实际应用场景

• 假设模组添加了一个名为“袋鼠”的生物,需要在沙漠和草原群系中生成。
• 在 AMWorldRegistry.addBiomeSpawns 中,会根据生物群系类型(通过 biome 参数判断)调用类似 builder.getMobSpawnSettings().addSpawn(...) 的代码,添加生成规则。


总结

这段代码是 Alex’s Mobs 模组中用于动态管理生物生成的核心逻辑,利用 Forge 的 BiomeModifier 系统,在生物群系加载时注入自定义生成规则。通过 Codec 实现数据驱动,使得生物生成配置更加灵活且易于维护。

和Biometag的联系:

这两段代码分别属于 生物群系(Biome)的动态修改生物群系的标签分类 系统,在 Minecraft 模组开发中密切相关但功能不同。以下是它们的联系与区别分析:


联系

  1. 共同目标
    两者均围绕 生物群系的特性管理,但角度不同:
    AMMobSpawnBiomeModifier动态添加生物生成规则到符合条件的生物群系。
    BiomeTags定义生物群系的分类标签(如是否为海洋、森林,是否生成特定结构等),供游戏逻辑判断。

  2. 潜在依赖关系
    AMMobSpawnBiomeModifier 在实现时 可能依赖 BiomeTags 的标签,例如:

    // 假设在 AMWorldRegistry.addBiomeSpawns 中:
    if (biome.is(BiomeTags.IS_FOREST)) {
         
        builder.getMobSpawnSettings().addSpawn(...); // 仅在森林群系添加生物
    }
    

    • 通过 BiomeTags 的标签,可以精确控制生物生成的目标群系。

  3. 数据驱动协同
    BiomeTags 提供 静态标签分类(如 IS_OCEAN),用于描述生物群系的属性。
    AMMobSpawnBiomeModifier 利用这些标签 动态注入逻辑(如只在海洋群系生成某种鱼类)。


区别

特性 AMMobSpawnBiomeModifier (模组代码) BiomeTags (原版代码)
功能 动态修改生物群系的生成规则(如添加生物) 定义生物群系的分类标签(元数据)
所属系统 Forge 的 BiomeModifier 系统 原版 Minecraft 的标签系统(TagKey
实现方式 通过 BiomeModifier 接口和 Codec 序列化 静态定义 TagKey 常量
数据驱动 可能依赖 JSON 配置或代码硬编码规则 标签通过资源包或数据包动态配置
代码层级 模组业务逻辑(添加自定义内容) 原版基础设施(提供全局分类标准)
典型应用场景 模组生物的生成控制 原版结构生成、天气逻辑、生物行为等

核心细节解析

  1. BiomeTags 的作用
    • 用于标记生物群系的属性,例如:

    public static final TagKey<Biome> IS_OCEAN = create("is_ocean");
    public static final TagKey<Biome> HAS_OCEAN_MONUMENT = create("has_structure/ocean_monument");
    

    • 游戏逻辑可通过这些标签快速判断群系特性,例如:
    ◦ 海洋神庙只生成在 HAS_OCEAN_MONUMENT 标签的群系。
    ◦ 溺尸生成频率可能在 MORE_FREQUENT_DROWNED_SPAWNS 标签的群系中提高。

  2. AMMobSpawnBiomeModifier 的工作流程
    注册序列化器:通过 SERIALIZER 声明该修改器的配置方式。
    修改阶段:在 Phase.ADD 阶段调用 AMWorldRegistry.addBiomeSpawns,向群系注入生物生成规则。
    潜在标签依赖:若模组生物需在特定标签的群系生成(如森林),需通过 BiomeTags.IS_FOREST 判断。


协作示例

假设模组要添加一个 “森林精灵”生物,仅在森林群系(IS_FOREST)且无巡逻队生成(WITHOUT_PATROL_SPAWNS)的群系中出现:

  1. BiomeTags:利用现有标签 IS_FORESTWITHOUT_PATROL_SPAWNS
  2. AMMobSpawnBiomeModifier
    public void modify(Holder<Biome> biome, Phase phase, ModifiableBiomeInfo.BiomeInfo.Builder builder) {
         
        if (phase == Phase.ADD 
            && biome.is(BiomeTags.IS_FOREST) 
            && biome.is(BiomeTags.WITHOUT_PATROL_SPAWNS)) {
         
            builder.getMobSpawnSettings().addSpawn(...); // 添加“森林精灵”生成规则
        }
    }
    
    • 直接依赖 BiomeTags 的标签实现精准控制。

总结

BiomeTags 是“群系属性字典”:提供全局的群系分类标准,供游戏和模组共用。
AMMobSpawnBiomeModifier 是“动态规则引擎”:利用标签或硬编码逻辑,向群系注入自定义内容。
二者协作:标签系统为动态修改提供判断依据,动态修改器实现具体游戏内容的变化。
(例如:原版用标签控制结构生成,模组用同一标签控制生物生成,实现无缝兼容。)

ModifiableBiomeInfo类

这段代码是 Minecraft Forge 中用于动态修改生物群系(Biome)属性的核心类 ModifiableBiomeInfo,其原理围绕 封装生物群系数据应用模组修改逻辑 展开。以下是代码的详细分析:


1. 类结构与作用

ModifiableBiomeInfo

作用
封装一个生物群系的原始数据,并提供接口让模组(Mod)按阶段动态修改其属性(如气候、特效、地形生成规则、刷怪规则等)。
成员变量
originalBiomeInfo:原始生物群系数据(不可变)。
modifiedBiomeInfo:修改后的生物群系数据(初始为 null,应用修改后生成)。

BiomeInfo 内部记录类(Record)

作用
存储生物群系的完整配置,包含四个核心属性:
climateSettings:气候设置(温度、降水量等)。
effects:视觉效果(天空颜色、水颜色等)。
generationSettings:地形生成规则(如结构、植被)。
mobSpawnSettings:刷怪规则(生物种类、权重等)。

Builder 内部类

作用
基于建造者模式(Builder Pattern)逐步构建 BiomeInfo 对象,允许复制原始数据并分步骤修改。


2. 核心方法解析

applyBiomeModifiers 方法
public void applyBiomeModifiers(Holder<Biome> biome, List<BiomeModifier> biomeModifiers) {
   
    if (this.modifiedBiomeInfo != null) {
   
        throw new IllegalStateException("Biome already modified");
    }
    BiomeInfo original = this.getOriginalBiomeInfo();
    BiomeInfo.Builder builder = BiomeInfo.Builder.copyOf(original); // 复制原始数据

    // 按阶段(Phase)应用所有 BiomeModifier
    for (Phase phase : Phase.values()) {
   
        for (BiomeModifier modifier : biomeModifiers) {
   
            modifier.modify(biome, phase, builder); // 调用修改器
        }
    }

    this.modifiedBiomeInfo = builder.build(); // 构建修改后的数据
}

流程

  1. 检查是否已修改:若已修改则抛出异常,避免重复修改。
  2. 复制原始数据:通过 Builder.copyOf 创建一个可修改的副本。
  3. 按阶段应用修改器:遍历所有 BiomeModifier,并按阶段(如 ADDREMOVEMODIFY)调用其 modify 方法。
  4. 构建最终数据:将修改后的 Builder 转换为不可变的 BiomeInfo

关键点
阶段(Phase):允许不同模组按顺序修改生物群系。例如:
ADD 阶段添加新规则。
MODIFY 阶段调整现有规则。
REMOVE 阶段删除某些规则。
不可变数据:修改后的 BiomeInfo 是不可变对象,确保线程安全和数据一致性。


3. 设计模式与原理

建造者模式(Builder Pattern)

BiomeInfo.Builder
提供逐步构建 BiomeInfo 的能力,通过方法链式调用修改各个属性:

BiomeInfo.Builder builder = BiomeInfo.Builder.copyOf(original);
builder.getClimateSettings().setTemperature(0.5f);
builder.getMobSpawnSettings().addSpawn(...);
BiomeInfo modified = builder.build();

优点
将对象构建与表示分离,支持灵活修改且避免参数爆炸。

不可变对象(Immutable Object)

BiomeInfo 为记录类(Record)
所有字段在构造后不可变,确保在多线程环境下无需同步即可安全访问。
修改流程
通过复制原始数据生成新对象,避免直接修改原数据。

事件驱动修改

BiomeModifier 接口
模组通过实现此接口并注册到 Forge 事件系统,在游戏加载生物群系时动态修改其属性。例如:

public class MyBiomeModifier implements BiomeModifier {
   
    @Override
    public void modify(Holder<Biome> biome, Phase phase, BiomeInfo.Builder builder) {
   
        if (phase == Phase.ADD) {
   
            builder.getMobSpawnSettings().addSpawn(...); // 添加新生物
        }
    }
}

4. 与 Alex’s Mobs 的关联

在用户之前提供的 AMWorldRegistry 代码中,addBiomeSpawns 方法可能通过注册 BiomeModifier 来修改生物群系的刷怪规则。例如:

// 伪代码:注册一个 BiomeModifier
BiomeModifierManager.addModifier(context -> {
   
    context.getBuilder().getMobSpawnSettings().addSpawn(...); // 添加模组生物
});

ModifiableBiomeInfo 类是这一过程的底层支持,负责协调所有模组的修改请求,并按阶段生成最终配置。


5. 总结

功能
ModifiableBiomeInfo 是 Forge 提供的核心工具类,用于实现生物群系属性的动态修改,支持多模组协作。
设计亮点
• 使用建造者模式逐步构建不可变数据。
• 分阶段应用修改器,确保逻辑顺序和兼容性。
• 通过不可变对象保证线程安全。
应用场景
模组开发者通过注册 BiomeModifier,在游戏加载时修改生物群系的气候、刷怪规则、地形生成等,实现自定义内容与原版或其他模组的无缝集成。

Biome类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

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 javax.annotation.Nullable;
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.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 net.minecraftforge.common.world.ModifiableBiomeInfo;

public final class Biome {
   
    public static final Codec<Biome> DIRECT_CODEC = RecordCodecBuilder.create((p_220544_) -> p_220544_.group(Biome.ClimateSettings.CODEC.forGetter((p_151717_) -> p_151717_.modifiableBiomeInfo().getOriginalBiomeInfo().climateSettings()), BiomeSpecialEffects.CODEC.fieldOf("effects").forGetter((p_220550_) -> p_220550_.modifiableBiomeInfo().getOriginalBiomeInfo().effects()), BiomeGenerationSettings.CODEC.forGetter((p_220548_) -> p_220548_.generationSettings), MobSpawnSettings.CODEC.forGetter((p_220546_) -> p_220546_.mobSettings)).apply(p_220544_, Biome::new));
    public static final Codec<Biome> NETWORK_CODEC = RecordCodecBuilder.create((p_220540_) -> p_220540_.group(Biome.ClimateSettings.CODEC.forGetter((p_220542_) -> p_220542_.climateSettings), BiomeSpecialEffects.CODEC.fieldOf("effects").forGetter((p_220538_) -> p_220538_.specialEffects)).apply(p_220540_, (p_220535_, p_220536_) -> new Biome(p_220535_, p_220536_, BiomeGenerationSettings.EMPTY, MobSpawnSettings.EMPTY)));
    public static final Codec<Holder<Biome>> CODEC;
    public static final Codec<HolderSet<Biome>> LIST_CODEC;
    private static final PerlinSimplexNoise TEMPERATURE_NOISE;
    static final PerlinSimplexNoise FROZEN_TEMPERATURE_NOISE;
    /** @deprecated */
    @Deprecated(
        forRemoval = true
    )
    public static final PerlinSimplexNoise BIOME_INFO_NOISE;
    private static final int TEMPERATURE_CACHE_SIZE = 1024;
    private final ClimateSettings climateSettings;
    private final BiomeGenerationSettings generationSettings;
    private final MobSpawnSettings mobSettings;
    private final BiomeSpecialEffects specialEffects;
    private final ThreadLocal<Long2FloatLinkedOpenHashMap> temperatureCache = ThreadLocal.withInitial(() -> (Long2FloatLinkedOpenHashMap)Util.make(() -> {
   
            Long2FloatLinkedOpenHashMap long2floatlinkedopenhashmap = new Long2FloatLinkedOpenHashMap(1024, 0.25F) {
   
                protected void rehash(int p_47580_) {
   
                }
            };
            long2floatlinkedopenhashmap.defaultReturnValue(Float.NaN);
            return long2floatlinkedopenhashmap;
        }));
    private final ModifiableBiomeInfo modifiableBiomeInfo;

    Biome(ClimateSettings p_220530_, BiomeSpecialEffects p_220531_, BiomeGenerationSettings p_220532_, MobSpawnSettings p_220533_) {
   
        this.climateSettings = p_220530_;
        this.generationSettings = p_220532_;
        this.mobSettings = p_220533_;
        this.specialEffects = p_220531_;
        this.modifiableBiomeInfo = new ModifiableBiomeInfo(new ModifiableBiomeInfo.BiomeInfo(p_220530_, p_220531_, p_220532_, p_220533_));
    }

    public int getSkyColor() {
   
        return this.specialEffects.getSkyColor();
    }

    public MobSpawnSettings getMobSettings() {
   
        return this.modifiableBiomeInfo().get().mobSpawnSettings();
    }

    public boolean hasPrecipitation() {
   
        return this.climateSettings.hasPrecipitation();
    }

    public Precipitation getPrecipitationAt(BlockPos p_265163_) {
   
        if (!this.hasPrecipitation()) {
   
            return Biome.Precipitation.NONE;
        } else {
   
            return this.coldEnoughToSnow(p_265163_) ? Biome.Precipitation.SNOW : Biome.Precipitation.RAIN;
        }
    }

    private float getHeightAdjustedTemperature(BlockPos p_47529_) {
   
        float f = this.climateSettings.temperatureModifier.modifyTemperature(p_47529_, this.getBaseTemperature());
        if (p_47529_.getY() > 80) {
   
            float f1 = (float)(TEMPERATURE_NOISE.getValue((double)((float)p_47529_.getX() / 8.0F), (double)((float)p_47529_.getZ() / 8.0F), false) * (double)8.0F);
            return f - (f1 + (float)p_47529_.getY() - 80.0F) * 0.05F / 40.0F;
        } else {
   
            return f;
        }
    }

    /** @deprecated */
    @Deprecated
    private float getTemperature(BlockPos p_47506_) {
   
        long i = p_47506_.asLong();
        Long2FloatLinkedOpenHashMap long2floatlinkedopenhashmap = (Long2FloatLinkedOpenHashMap)this.temperatureCache.get();
        float f = long2floatlinkedopenhashmap.get(i);
        if (!Float.isNaN(f)) {
   
            return f;
        } else {
   
            float f1 = this.getHeightAdjustedTemperature(p_47506_);
            if (long2floatlinkedopenhashmap.size() == 1024) {
   
                long2floatlinkedopenhashmap.removeFirstFloat();
            }

            long2floatlinkedopenhashmap.put(i, f1);
            return f1;
        }
    }

    public boolean shouldFreeze(LevelReader p_47478_, BlockPos p_47479_) {
   
        return this.shouldFreeze(p_47478_, p_47479_, true);
    }

    public boolean shouldFreeze(LevelReader p_47481_, BlockPos p_47482_, boolean p_47483_) {
   
        if (this.warmEnoughToRain(p_47482_)) {
   
            return false;
        } else {
   
            if (p_47482_.getY() >= p_47481_.getMinBuildHeight() && p_47482_.getY() < p_47481_.getMaxBuildHeight() && p_47481_.getBrightness(LightLayer.BLOCK, p_47482_) < 10) {
   
                BlockState blockstate = p_47481_.getBlockState(p_47482_);
                FluidState fluidstate = p_47481_.getFluidState(p_47482_);
                if (fluidstate.getType() == Fluids.WATER && blockstate.getBlock() instanceof LiquidBlock) {
   
                    if (!p_47483_) {
   
                        return true;
                    }

                    boolean flag = p_47481_.isWaterAt(p_47482_.west()) && p_47481_.isWaterAt(p_47482_.east()) && p_47481_.isWaterAt(p_47482_.north()) && p_47481_.isWaterAt(p_47482_.south());
                    if (!flag) {
   
                        return true;
                    }
                }
            }

            return false;
        }
    }

    public boolean coldEnoughToSnow(BlockPos p_198905_) {
   
        return !this.warmEnoughToRain(p_198905_);
    }

    public boolean warmEnoughToRain(BlockPos p_198907_) {
   
        return this.getTemperature(p_198907_) >= 0.15F;
    }

    public boolean shouldMeltFrozenOceanIcebergSlightly(BlockPos p_198909_) {
   
        return this.getTemperature(p_198909_) > 0.1F;
    }

    public boolean shouldSnow(LevelReader p_47520_, BlockPos p_47521_) {
   
        if (this.warmEnoughToRain(p_47521_)) {
   
            return false;
        } else {
   
            if (p_47521_.getY() >= p_47520_.getMinBuildHeight() && p_47521_.getY() < p_47520_.getMaxBuildHeight() && p_47520_.getBrightness(LightLayer.BLOCK, p_47521_) < 10) {
   
                BlockState blockstate = p_47520_.getBlockState(p_47521_);
                if ((blockstate.isAir() || blockstate.is(Blocks.SNOW)) && Blocks.SNOW.defaultBlockState().canSurvive(p_47520_, p_47521_)) {
   
                    return true;
                }
            }

            return false;
        }
    }

    public BiomeGenerationSettings getGenerationSettings() {
   
        return this.modifiableBiomeInfo().get().generationSettings();
    }

    public int getFogColor() {
   
        return this.specialEffects.getFogColor();
    }

    public int getGrassColor(double p_47465_, double p_47466_) {
   
        int i = (Integer)this.specialEffects.getGrassColorOverride().orElseGet(this::getGrassColorFromTexture);
        return this.specialEffects.getGrassColorModifier().modifyColor(p_47465_, p_47466_, i);
    }

    private int getGrassColorFromTexture() {
   
        double d0 = (double)Mth.clamp(this.climateSettings.temperature, 0.0F, 1.0F);
        double d1 = (double)Mth.clamp(this.climateSettings.downfall, 0.0F, 1.0F);
        return GrassColor.get(d0, d1);
    }

    public int getFoliageColor() {
   
        return (Integer)this.specialEffects.getFoliageColorOverride().orElseGet(this::getFoliageColorFromTexture);
    }

    private int getFoliageColorFromTexture() {
   
        double d0 = (double)Mth.clamp(this.climateSettings.temperature, 0.0F, 1.0F);
        double d1 = (double)Mth.clamp(this.climateSettings.downfall, 0.0F, 1.0F);
        return FoliageColor.get(d0, d1);
    }

    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<Music> getBackgroundMusic() {
   
        return this.specialEffects.getBackgroundMusic();
    }

    public ModifiableBiomeInfo modifiableBiomeInfo() {
   
        return this.modifiableBiomeInfo;
    }

    public ClimateSettings getModifiedClimateSettings() {
   
        return this.modifiableBiomeInfo().get().climateSettings();
    }

    public BiomeSpecialEffects getModifiedSpecialEffects() {
   
        return this.modifiableBiomeInfo().get().effects();
    }

    static {
   
        CODEC = RegistryFileCodec.create(Registries.BIOME, DIRECT_CODEC);
        LIST_CODEC = RegistryCodecs.homogeneousList(Registries.BIOME, DIRECT_CODEC);
        TEMPERATURE_NOISE = new PerlinSimplexNoise(new WorldgenRandom(new LegacyRandomSource(1234L)), ImmutableList.of(0));
        FROZEN_TEMPERATURE_NOISE = new PerlinSimplexNoise(new WorldgenRandom(new LegacyRandomSource(3456L)), ImmutableList.of(-2, -1, 0));
        BIOME_INFO_NOISE = new PerlinSimplexNoise(new WorldgenRandom(new LegacyRandomSource(2345L)), ImmutableList.of(0));
    }

    public static class BiomeBuilder {
   
        private boolean hasPrecipitation = true;
        @Nullable
        private Float temperature;
        private TemperatureModifier temperatureModifier;
        @Nullable
        private Float downfall;
        @Nullable
        private BiomeSpecialEffects specialEffects;
        @Nullable
        private MobSpawnSettings mobSpawnSettings;
        @Nullable
        private BiomeGenerationSettings generationSettings;

        public BiomeBuilder() {
   
            this.temperatureModifier = Biome.TemperatureModifier.NONE;
        }

        public BiomeBuilder hasPrecipitation(boolean p_265480_) {
   
            this.hasPrecipitation = p_265480_;
            return this;
        }

        public BiomeBuilder temperature(float p_47610_) {
   
            this.temperature = p_47610_;
            return this;
        }

        public BiomeBuilder downfall(float p_47612_) {
   
            this.downfall = p_47612_;
            return this;
        }

        public BiomeBuilder specialEffects(BiomeSpecialEffects p_47604_) {
   
            this.specialEffects = p_47604_;
            return this;
        }

        public BiomeBuilder mobSpawnSettings(MobSpawnSettings p_47606_) {
   
            this.mobSpawnSettings = p_47606_;
            return this;
        }

        public BiomeBuilder generationSettings(BiomeGenerationSettings p_47602_) {
   
            this.generationSettings = p_47602_;
            return this;
        }

        public BiomeBuilder temperatureAdjustment(TemperatureModifier p_47600_) {
   
            this.temperatureModifier = p_47600_;
            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 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 

相关文章:

  • 还在用Excel规划机房变更吗?
  • VSCode 出现一直Reactivating terminals,怎么破
  • ubuntu服务器server版安装,ssh远程连接xmanager管理,改ip网络连接。图文教程
  • “浅浅深究”一下ConcurrentHashMap
  • 12-scala样例类(Case Classes)
  • DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加导出数据功能示例14,TableView15_14多功能组合的导出表格示例
  • 使用 ByteDance 的 UI-TARS Desktop 探索 AI 驱动的 GUI 自动化新前沿
  • 1007 Maximum Subsequence Sum
  • 如何在IDEA中借助深度思考模型 QwQ 提高编码效率?
  • DeepSeek+RAG局域网部署
  • 微软纳德拉最新一期访谈
  • 如何删除git上最后一次提交,Git日常使用操作说明。
  • python高级4
  • Mysql从入门到精通day3————记一次连接查询的武装渗透
  • 【二分查找 树状数组 差分数组 离散化 】P6172 [USACO16FEB] Load Balancing P|省选-
  • 牛顿-拉夫逊迭代法原理与除法器的软件与硬件实现
  • 六十天Linux从0到项目搭建第四天(通配符命令、其他命令、压缩解压工具、shell的感性理解、linux权限解析)
  • 从零实现本地文生图部署(Stable Diffusion)
  • 常见框架漏洞攻略-ThinkPHP篇
  • 人工智能AI术语
  • 广州一饮品店取名“警茶”?市监局:取名没问题,但图像会产生误解
  • 事关心脏健康安全,经导管植入式人工心脏瓣膜国家标准发布
  • 珠峰窗口期5月开启 普通人登一次有多烧钱?
  • 苹果或将于2027年推出由玻璃制成的曲面iPhone
  • 郑州通报“夜市摊贩收取香烟交给城管”:涉事人员停职调查
  • 中国工程院院士、国医大师、现代中国针灸奠基人石学敏逝世