diff --git a/core/src/main/java/art/arcane/iris/pack/ingest/ModBiomeAdopter.java b/core/src/main/java/art/arcane/iris/pack/ingest/ModBiomeAdopter.java index f0f4d91af..8edf1a4c2 100644 --- a/core/src/main/java/art/arcane/iris/pack/ingest/ModBiomeAdopter.java +++ b/core/src/main/java/art/arcane/iris/pack/ingest/ModBiomeAdopter.java @@ -85,7 +85,7 @@ final class ModBiomeAdopter { transform(json, biome, ownsVegetation(biome, namespace)); model.pack().create(IngestPack.biomePath(irisKey), json); appendToPool(region.key(), role, irisKey); - adopted.put(biome.key(), new AdoptedBiome(biome.key(), irisKey, role)); + adopted.put(biome.key(), new AdoptedBiome(biome.key(), irisKey, role, region.key())); items.add(new ModIngestItem(namespace, biome.key(), "biome", ModIngestTier.ADOPTED, irisKey, role.label() + " biome in region " + region.key() + " from template " + template)); } @@ -196,7 +196,7 @@ final class ModBiomeAdopter { return name.isEmpty() ? key : name.toString(); } - record AdoptedBiome(String modKey, String irisKey, BiomeRole role) { + record AdoptedBiome(String modKey, String irisKey, BiomeRole role, String regionKey) { } private record Placement(IngestPackModel.RegionProfile region, String template) { diff --git a/core/src/main/java/art/arcane/iris/pack/ingest/ModFeatureIngester.java b/core/src/main/java/art/arcane/iris/pack/ingest/ModFeatureIngester.java index 7628b1b7d..7ff8c5a9a 100644 --- a/core/src/main/java/art/arcane/iris/pack/ingest/ModFeatureIngester.java +++ b/core/src/main/java/art/arcane/iris/pack/ingest/ModFeatureIngester.java @@ -221,36 +221,53 @@ final class ModFeatureIngester { } private String applyDeposits(FeaturePlan plan, Map adopted, Set universe, Set caveKeys) throws IOException { - List adoptedTargets = new ArrayList<>(); + List adoptedTargets = new ArrayList<>(); boolean vanillaDeclared = false; for (String biome : plan.declaring()) { vanillaDeclared |= WorldgenCatalogIndex.namespaceOf(biome).equals("minecraft"); ModBiomeAdopter.AdoptedBiome adoptedBiome = adopted.get(biome); if (adoptedBiome != null) { - adoptedTargets.add(adoptedBiome.irisKey()); + adoptedTargets.add(adoptedBiome); } } if (!vanillaDeclared && !adoptedTargets.isEmpty()) { - for (String irisKey : adoptedTargets) { - appendAll(model.pack().require(IngestPack.biomePath(irisKey)), "deposits", plan.deposits()); + List targets = new ArrayList<>(adoptedTargets.size()); + Map> caveBiomesByRegion = new TreeMap<>(); + for (ModBiomeAdopter.AdoptedBiome target : adoptedTargets) { + if (target.role() == BiomeRole.CAVE) { + caveBiomesByRegion.computeIfAbsent(target.regionKey(), ignored -> new ArrayList<>()).add(target.irisKey()); + continue; + } + appendAll(model.pack().require(IngestPack.biomePath(target.irisKey())), "deposits", plan.deposits()); + targets.add(target.irisKey()); } - return String.join(",", adoptedTargets); + for (Map.Entry> region : caveBiomesByRegion.entrySet()) { + appendAll(model.pack().edit(IngestPack.regionPath(region.getKey())), "deposits", + scoped(plan.deposits(), region.getValue(), "CAVE")); + targets.add("region " + region.getKey()); + } + return String.join(",", targets); } Set included = new TreeSet<>(plan.declaring()); included.retainAll(universe); - List deposits = new ArrayList<>(plan.deposits().size()); - for (JsonObject deposit : plan.deposits()) { - JsonObject copy = deposit.deepCopy(); - if (!included.containsAll(universe)) { - copy.add("includedBiomes", IngestJson.stringArray(included)); - copy.addProperty("biomeScope", caveKeys.containsAll(included) ? "CAVE" : "SURFACE"); - } - deposits.add(copy); - } + List deposits = included.containsAll(universe) + ? plan.deposits() + : scoped(plan.deposits(), List.copyOf(included), caveKeys.containsAll(included) ? "CAVE" : "SURFACE"); appendAll(model.pack().edit(model.dimensionPath()), "deposits", deposits); return "dimension"; } + private static List scoped(List deposits, List includedBiomes, String biomeScope) { + List scoped = new ArrayList<>(deposits.size()); + for (JsonObject deposit : deposits) { + JsonObject copy = deposit.deepCopy(); + copy.add("includedBiomes", IngestJson.stringArray(includedBiomes)); + copy.addProperty("biomeScope", biomeScope); + scoped.add(copy); + } + return scoped; + } + private String applyDecorator(FeaturePlan plan, Map adopted) throws IOException { List targets = new ArrayList<>(); for (String biome : plan.declaring()) { diff --git a/core/src/test/java/art/arcane/iris/pack/ingest/IngestCaveOreTest.java b/core/src/test/java/art/arcane/iris/pack/ingest/IngestCaveOreTest.java new file mode 100644 index 000000000..922e6f908 --- /dev/null +++ b/core/src/test/java/art/arcane/iris/pack/ingest/IngestCaveOreTest.java @@ -0,0 +1,99 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package art.arcane.iris.pack.ingest; + +import art.arcane.iris.pack.ingest.catalog.CatalogBiome; +import art.arcane.iris.pack.ingest.catalog.CatalogPlacedFeature; +import art.arcane.iris.pack.ingest.catalog.WorldgenCatalog; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class IngestCaveOreTest { + private static final String MOD = ModIngestFixture.MOD; + + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + + @Test + public void oresOfAdoptedCaveBiomesRunFromTheRegionScopedToThoseCaves() throws Exception { + Path root = folder.getRoot().toPath(); + Path source = ModIngestFixture.writeSourcePack(root); + Path target = root.resolve("packs").resolve("caves"); + + ModIngestReport report = ModIngester.ingest(new ModIngestRequest(source, target, "overworld", catalog(), Set.of(MOD), Set.of(), false)); + + JsonArray regionDeposits = ModIngestFixture.readJson(target.resolve("regions/hot.json")).getAsJsonArray("deposits"); + assertNotNull(regionDeposits); + JsonObject geode = ModIngestFixture.findByPaletteBlock(regionDeposits, MOD + ":geode_ore"); + assertNotNull(geode); + assertEquals(List.of("ingest/" + MOD + "/crystal_caves"), ModIngestFixture.strings(geode.get("includedBiomes"))); + assertEquals("CAVE", geode.get("biomeScope").getAsString()); + JsonElement caveDeposits = ModIngestFixture.readJson(target.resolve("biomes/ingest/" + MOD + "/crystal_caves.json")).get("deposits"); + assertTrue(caveDeposits == null || ModIngestFixture.findByPaletteBlock(caveDeposits.getAsJsonArray(), MOD + ":geode_ore") == null); + assertNull(ModIngestFixture.findByPaletteBlock(ModIngestFixture.readJson(target.resolve("dimensions/caves.json")).getAsJsonArray("deposits"), MOD + ":geode_ore")); + ModIngestItem item = ModIngestFixture.item(report, MOD + ":geode_ore"); + assertEquals(ModIngestTier.NATIVE, item.tier()); + assertTrue(item.target(), item.target().contains("region hot")); + assertFalse(ModIngestFixture.readJson(target.resolve("regions/temperate.json")).has("deposits")); + } + + private static WorldgenCatalog catalog() { + WorldgenCatalog fixture = ModIngestFixture.catalog(); + List biomes = new ArrayList<>(); + for (CatalogBiome biome : fixture.biomes()) { + if (!biome.key().equals(MOD + ":crystal_caves")) { + biomes.add(biome); + continue; + } + List> steps = new ArrayList<>(biome.featureSteps()); + List ores = new ArrayList<>(steps.get(ModIngestFixture.ORE_STEP)); + ores.add(MOD + ":geode_ore"); + steps.set(ModIngestFixture.ORE_STEP, ores); + biomes.add(new CatalogBiome(biome.key(), biome.temperature(), biome.downfall(), true, biome.tags(), steps, biome.spawns(), null)); + } + List features = new ArrayList<>(fixture.placedFeatures()); + String key = MOD + ":geode_ore"; + features.add(new CatalogPlacedFeature(key, key, "minecraft:ore", JsonParser.parseString(""" + {"size": 6, "targets": [{"state": "examplemod:geode_ore", "target": {"predicate_type": "minecraft:block_match", "block": "minecraft:stone"}}]} + """), JsonParser.parseString(""" + [{"type": "minecraft:count", "count": 3}, {"type": "minecraft:in_square"}, + {"type": "minecraft:height_range", "height": {"type": "minecraft:uniform", "min_inclusive": {"absolute": -40}, "max_inclusive": {"absolute": 20}}}, + {"type": "minecraft:biome"}] + """))); + return new WorldgenCatalog(fixture.format(), fixture.minecraftVersion(), fixture.platform(), fixture.capturedAtEpochMillis(), + fixture.contentFingerprint(), fixture.namespaces(), biomes, features, fixture.structures(), fixture.dimensionTypes(), + fixture.levelStems(), fixture.blockTags()); + } +}