Kept mod biomes findable

- Themed regions, those at least five times rarer than the dimension's median region, no longer receive adopted biomes while a common region accepts the biome's role
- Themed regions still take a mod biome when no common region has a pool for its role or every common template is full
- Category affinity now chooses among common regions, so a rare biome is no longer locked inside a region that can be absent for thousands of blocks
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-15 09:14:20 -04:00
1 parent 523cc7e7be
commit 137724157a
2 files changed
+62 -22

No files matched your search

@@ -45,7 +45,6 @@ final class ModBiomeAdopter {
private static final double REGION_CLIMATE_WEIGHT = 0.25D; private static final double REGION_CLIMATE_WEIGHT = 0.25D;
private static final double REGION_CATEGORY_WEIGHT = 0.25D; private static final double REGION_CATEGORY_WEIGHT = 0.25D;
private static final double REUSE_WEIGHT = 0.05D; private static final double REUSE_WEIGHT = 0.05D;
private static final double THEMED_REGION_PENALTY = 0.6D;
private static final double BARE_SURFACE_PENALTY = 3D; private static final double BARE_SURFACE_PENALTY = 3D;
private static final int THEMED_RARITY_FACTOR = 5; private static final int THEMED_RARITY_FACTOR = 5;
@@ -102,15 +101,20 @@ final class ModBiomeAdopter {
} }
private Placement place(BiomeRole role, IngestClimate climate, Set<String> categories, boolean ownsVegetation) throws IOException { private Placement place(BiomeRole role, IngestClimate climate, Set<String> categories, boolean ownsVegetation) throws IOException {
Placement common = bestPlacement(role, climate, categories, ownsVegetation, false);
return common != null ? common : bestPlacement(role, climate, categories, ownsVegetation, true);
}
private Placement bestPlacement(BiomeRole role, IngestClimate climate, Set<String> categories, boolean ownsVegetation,
boolean themed) throws IOException {
Placement best = null; Placement best = null;
double bestScore = Double.POSITIVE_INFINITY; double bestScore = Double.POSITIVE_INFINITY;
for (IngestPackModel.RegionProfile region : model.regions()) { for (IngestPackModel.RegionProfile region : model.regions()) {
if (excludedRegions.contains(region.key())) { if (excludedRegions.contains(region.key()) || themedRegions.contains(region.key()) != themed) {
continue; continue;
} }
IngestClimate regionClimate = regionClimate(region, role, categories); IngestClimate regionClimate = regionClimate(region, role, categories);
double regionPenalty = (themedRegions.contains(region.key()) ? THEMED_REGION_PENALTY : 0D) double regionPenalty = (regionClimate == null ? 0D : REGION_CLIMATE_WEIGHT * climate.distance(regionClimate))
+ (regionClimate == null ? 0D : REGION_CLIMATE_WEIGHT * climate.distance(regionClimate))
+ REGION_CATEGORY_WEIGHT * (1D - regionAffinity(region, role, categories)); + REGION_CATEGORY_WEIGHT * (1D - regionAffinity(region, role, categories));
for (String template : region.pool(role)) { for (String template : region.pool(role)) {
int uses = templateUses.getOrDefault(template, 0); int uses = templateUses.getOrDefault(template, 0);
@@ -33,7 +33,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
public class IngestRegionAffinityTest { public class IngestRegionAffinityTest {
private static final String MOD = "modisles"; private static final String MOD = "modisles";
@@ -43,18 +44,41 @@ public class IngestRegionAffinityTest {
@Test @Test
public void modBiomeJoinsTheRegionWhoseTemplatesShareItsCategory() throws Exception { public void modBiomeJoinsTheRegionWhoseTemplatesShareItsCategory() throws Exception {
Path root = folder.getRoot().toPath(); ModIngestReport report = ingest(1, false);
assertEquals("mushroom", region(report, "mirage_isles"));
}
@Test
public void rareThemedRegionNeverHostsAModBiomeWhileACommonRegionAcceptsIt() throws Exception {
ModIngestReport report = ingest(15, false);
String region = region(report, "mirage_isles");
assertNotEquals("mushroom", region);
assertNotEquals("magnetic", region);
}
@Test
public void themedRegionHostsAModBiomeWhenNoCommonRegionAcceptsItsRole() throws Exception {
ModIngestReport report = ingest(15, true);
assertEquals("mushroom", region(report, "spore_hollow"));
}
private ModIngestReport ingest(int themedRarity, boolean themedCaves) throws IOException {
Path root = folder.newFolder().toPath();
Path source = root.resolve("source"); Path source = root.resolve("source");
ModIngestFixture.write(source, "dimensions/overworld.json", """ ModIngestFixture.write(source, "dimensions/overworld.json", """
{"name": "Overworld", "environment": "NORMAL", "regions": ["temperate", "hot", "cold", "wet", "magnetic", "mushroom"], {"name": "Overworld", "environment": "NORMAL", "regions": ["temperate", "hot", "cold", "wet", "magnetic", "mushroom"],
"dimensionHeight": {"min": -64, "max": 320}} "dimensionHeight": {"min": -64, "max": 320}}
"""); """);
region(source, "temperate", 1, List.of("temperate/plains")); region(source, "temperate", 1, List.of("temperate/plains"), List.of());
region(source, "hot", 1, List.of("hot/desert")); region(source, "hot", 1, List.of("hot/desert"), List.of());
region(source, "cold", 1, List.of("cold/snow")); region(source, "cold", 1, List.of("cold/snow"), List.of());
region(source, "wet", 1, List.of("wet/swamp")); region(source, "wet", 1, List.of("wet/swamp"), List.of());
region(source, "magnetic", 15, List.of("magnetic/mycelium", "magnetic/plains", "magnetic/frozen", "magnetic/marsh")); region(source, "magnetic", themedRarity, List.of("magnetic/mycelium", "magnetic/plains", "magnetic/frozen", "magnetic/marsh"), List.of());
region(source, "mushroom", 15, List.of("mushroom/fields", "mushroom/forest", "mushroom/crimson", "mushroom/warped")); region(source, "mushroom", themedRarity, List.of("mushroom/fields", "mushroom/forest", "mushroom/crimson", "mushroom/warped"),
themedCaves ? List.of("mushroom/caves") : List.of());
biome(source, "temperate/plains", "minecraft:plains"); biome(source, "temperate/plains", "minecraft:plains");
biome(source, "hot/desert", "minecraft:desert"); biome(source, "hot/desert", "minecraft:desert");
biome(source, "cold/snow", "minecraft:snowy_plains"); biome(source, "cold/snow", "minecraft:snowy_plains");
@@ -67,20 +91,30 @@ public class IngestRegionAffinityTest {
biome(source, "mushroom/forest", "minecraft:mushroom_fields"); biome(source, "mushroom/forest", "minecraft:mushroom_fields");
biome(source, "mushroom/crimson", "minecraft:crimson_forest"); biome(source, "mushroom/crimson", "minecraft:crimson_forest");
biome(source, "mushroom/warped", "minecraft:warped_forest"); biome(source, "mushroom/warped", "minecraft:warped_forest");
biome(source, "mushroom/caves", "minecraft:lush_caves");
Path target = root.resolve("packs").resolve("isles"); Path target = root.resolve("packs").resolve("isles");
return ModIngester.ingest(new ModIngestRequest(source, target, "overworld", catalog(), Set.of(MOD), Set.of(), false));
ModIngestReport report = ModIngester.ingest(new ModIngestRequest(source, target, "overworld", catalog(), Set.of(MOD), Set.of(), false));
String reason = ModIngestFixture.item(report, MOD + ":mirage_isles").reason();
assertTrue(reason, reason.contains("in region mushroom "));
} }
private static void region(Path pack, String key, int rarity, List<String> land) throws IOException { private static String region(ModIngestReport report, String biome) {
StringBuilder pool = new StringBuilder(); ModIngestItem item = ModIngestFixture.item(report, MOD + ":" + biome);
for (String biome : land) { assertEquals(item.reason(), ModIngestTier.ADOPTED, item.tier());
pool.append(pool.isEmpty() ? "" : ", ").append('"').append(biome).append('"'); String reason = item.reason();
int start = reason.indexOf("in region ") + "in region ".length();
return reason.substring(start, reason.indexOf(' ', start));
}
private static void region(Path pack, String key, int rarity, List<String> land, List<String> caves) throws IOException {
ModIngestFixture.write(pack, "regions/" + key + ".json", "{\"name\": \"" + key + "\", \"rarity\": " + rarity
+ ", \"landBiomes\": " + quoted(land) + ", \"caveBiomes\": " + quoted(caves) + "}");
}
private static String quoted(List<String> keys) {
StringBuilder array = new StringBuilder("[");
for (String key : keys) {
array.append(array.length() == 1 ? "" : ", ").append('"').append(key).append('"');
} }
ModIngestFixture.write(pack, "regions/" + key + ".json", "{\"name\": \"" + key + "\", \"rarity\": " + rarity + ", \"landBiomes\": [" + pool + "]}"); return array.append(']').toString();
} }
private static void biome(Path pack, String key, String derivative) throws IOException { private static void biome(Path pack, String key, String derivative) throws IOException {
@@ -96,7 +130,9 @@ public class IngestRegionAffinityTest {
biomes.add(biome("minecraft:mushroom_fields", 0.9F, 1.0F, "c:is_mushroom")); biomes.add(biome("minecraft:mushroom_fields", 0.9F, 1.0F, "c:is_mushroom"));
biomes.add(biome("minecraft:crimson_forest", 2.0F, 0.0F, "c:is_nether")); biomes.add(biome("minecraft:crimson_forest", 2.0F, 0.0F, "c:is_nether"));
biomes.add(biome("minecraft:warped_forest", 2.0F, 0.0F, "c:is_nether")); biomes.add(biome("minecraft:warped_forest", 2.0F, 0.0F, "c:is_nether"));
biomes.add(biome("minecraft:lush_caves", 0.5F, 0.5F, "c:is_cave"));
biomes.add(biome(MOD + ":mirage_isles", 0.7F, 0.8F, "c:is_mushroom")); biomes.add(biome(MOD + ":mirage_isles", 0.7F, 0.8F, "c:is_mushroom"));
biomes.add(biome(MOD + ":spore_hollow", 0.9F, 1.0F, "c:is_cave"));
return new WorldgenCatalog(WorldgenCatalog.FORMAT, "26.3", "fabric", 1L, "affinity", return new WorldgenCatalog(WorldgenCatalog.FORMAT, "26.3", "fabric", 1L, "affinity",
List.of(new CatalogNamespace(MOD, "Mod Isles", "1")), biomes, List.of(), List.of(), List.of(new CatalogNamespace(MOD, "Mod Isles", "1")), biomes, List.of(), List.of(),
List.of(new CatalogDimensionType("minecraft:overworld", -64, 384, 384, null)), List.of(), Map.of()); List.of(new CatalogDimensionType("minecraft:overworld", -64, 384, 384, null)), List.of(), Map.of());