From 3821af4f82247f3947fd843c24da8183e1663493 Mon Sep 17 00:00:00 2001 From: gnembon Date: Sat, 2 May 2020 19:14:59 -0300 Subject: [PATCH] structure_eligibility and notes on spawn_potential --- docs/scarpet/Full.md | 19 ++++++ docs/scarpet/api/BlocksAndWorldAccess.md | 19 ++++++ .../java/carpet/helpers/FeatureGenerator.java | 58 ++++++++++++++----- .../java/carpet/script/CarpetExpression.java | 52 +++++++++++++++++ 4 files changed, 133 insertions(+), 15 deletions(-) diff --git a/docs/scarpet/Full.md b/docs/scarpet/Full.md index 5304d3ff..ad406637 100644 --- a/docs/scarpet/Full.md +++ b/docs/scarpet/Full.md @@ -2060,6 +2060,25 @@ if the chunk is not in memory unless called with optional `true`. Returns inhabited time for a chunk. +### `spawn_potential(pos)` + +Returns spawn potential at a location (1.16+ only) + +### `structure_eligibility(pos, ?structure, ?size_needed)` + +Checks wordgen eligibility for a structure in a given chunk. If no structure is given, or `null`, then it will check + for all structures. If bounding box of the structures is also requested, it will compute size of potential + structures. This function, unlike other in the `structure*` category is not using world data nor accesses chunks + making it preferred for scoping ungenerated terrain. + +If structure is specified, it will return `null` if a chunk is not eligible, `true` if the structure should appear, or +a pair of coordinates indicating bounding box of the structure. + +If structure is not specified, it will return a set of structure names that are eligible, or a map with structures +as keys, and pair of bounding box coordinates as values. An empty set or an empty map would indicate that nothing +should be generated there. + + ### `structures(pos), structures(pos, structure_name)` Returns structure information for a given block position. Note that structure information is the same for all the diff --git a/docs/scarpet/api/BlocksAndWorldAccess.md b/docs/scarpet/api/BlocksAndWorldAccess.md index 0847bbbf..17145b86 100644 --- a/docs/scarpet/api/BlocksAndWorldAccess.md +++ b/docs/scarpet/api/BlocksAndWorldAccess.md @@ -523,6 +523,25 @@ if the chunk is not in memory unless called with optional `true`. Returns inhabited time for a chunk. +### `spawn_potential(pos)` + +Returns spawn potential at a location (1.16+ only) + +### `structure_eligibility(pos, ?structure, ?size_needed)` + +Checks wordgen eligibility for a structure in a given chunk. If no structure is given, or `null`, then it will check + for all structures. If bounding box of the structures is also requested, it will compute size of potential + structures. This function, unlike other in the `structure*` category is not using world data nor accesses chunks + making it preferred for scoping ungenerated terrain. + +If structure is specified, it will return `null` if a chunk is not eligible, `true` if the structure should appear, or +a pair of coordinates indicating bounding box of the structure. + +If structure is not specified, it will return a set of structure names that are eligible, or a map with structures +as keys, and pair of bounding box coordinates as values. An empty set or an empty map would indicate that nothing +should be generated there. + + ### `structures(pos), structures(pos, structure_name)` Returns structure information for a given block position. Note that structure information is the same for all the diff --git a/src/main/java/carpet/helpers/FeatureGenerator.java b/src/main/java/carpet/helpers/FeatureGenerator.java index eb8caf20..6aaeea78 100644 --- a/src/main/java/carpet/helpers/FeatureGenerator.java +++ b/src/main/java/carpet/helpers/FeatureGenerator.java @@ -6,13 +6,19 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import net.minecraft.block.Blocks; import net.minecraft.server.world.ServerWorld; +import net.minecraft.structure.StructureManager; +import net.minecraft.structure.StructureStart; +import net.minecraft.util.math.BlockBox; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; import net.minecraft.world.biome.Biome; import net.minecraft.world.biome.Biomes; import net.minecraft.world.biome.DefaultBiomeFeatures; +import net.minecraft.world.biome.source.BiomeAccess; import net.minecraft.world.biome.source.BiomeSource; import net.minecraft.world.biome.source.VanillaLayeredBiomeSource; import net.minecraft.world.biome.source.VanillaLayeredBiomeSourceConfig; +import net.minecraft.world.gen.ChunkRandom; import net.minecraft.world.gen.chunk.ChunkGenerator; import net.minecraft.world.gen.chunk.OverworldChunkGenerator; import net.minecraft.world.gen.chunk.OverworldChunkGeneratorConfig; @@ -141,26 +147,48 @@ public class FeatureGenerator return null; } + public static BlockBox DEFAULT_BOX = new BlockBox(0,0,0,1,1,1); + public static BlockBox shouldStructureStartAt(ServerWorld world, BlockPos pos, StructureFeature structure, boolean computeBox) + { + ChunkGenerator generator = world.getChunkManager().getChunkGenerator(); + if (!generator.getBiomeSource().hasStructureFeature(structure)) + return null; + BiomeAccess biomeAccess = world.getBiomeAccess().withSource(generator.getBiomeSource()); + ChunkRandom chunkRandom = new ChunkRandom(); + ChunkPos chunkPos = new ChunkPos(pos); + Biome biome = biomeAccess.getBiome(new BlockPos(chunkPos.getStartX() + 9, 0, chunkPos.getStartZ() + 9)); + if (structure.shouldStartAt(biomeAccess, generator, chunkRandom, chunkPos.x, chunkPos.z, biome)) + { + if (!computeBox) return DEFAULT_BOX; + StructureManager manager = world.getSaveHandler().getStructureManager(); + StructureStart structureStart3 = structure.getStructureStartFactory().create(structure, chunkPos.x, chunkPos.z, BlockBox.empty(), 0, generator.getSeed()); + structureStart3.initialize(generator, manager, chunkPos.x, chunkPos.z, biome); + if (!structureStart3.hasChildren()) return null; + return structureStart3.getBoundingBox(); + } + return null; + } + public static final Map> structureToFeature = new HashMap<>(); public static final Map featureToStructure = new HashMap<>(); static { - structureToFeature.put("Monument", Collections.singletonList("monument")); - structureToFeature.put("EndCity", Collections.singletonList("end_city")); - structureToFeature.put("Ocean_Ruin", Arrays.asList("ocean_ruin", "ocean_ruin_warm", "ocean_ruin_small", "ocean_ruin_warm_small", "ocean_ruin_tall", "ocean_ruin_warm_tall")); - structureToFeature.put("Village", Arrays.asList("village", "village_desert", "village_savanna", "village_taiga", "village_snowy")); - structureToFeature.put("Mansion", Collections.singletonList("mansion")); - structureToFeature.put("Buried_Treasure", Collections.singletonList("treasure")); - structureToFeature.put("Swamp_Hut", Collections.singletonList("witch_hut")); - structureToFeature.put("Stronghold", Collections.singletonList("stronghold")); - structureToFeature.put("Desert_Pyramid", Collections.singletonList("desert_temple")); - structureToFeature.put("Jungle_Pyramid", Collections.singletonList("jungle_temple")); - structureToFeature.put("Shipwreck", Arrays.asList("shipwreck", "shipwreck2")); - structureToFeature.put("Pillager_Outpost", Collections.singletonList("pillager_outpost")); - structureToFeature.put("Mineshaft", Arrays.asList("mineshaft", "mineshaft_mesa")); - structureToFeature.put("Igloo", Collections.singletonList("igloo")); - structureToFeature.put("Fortress", Collections.singletonList("fortress")); + structureToFeature.put(Feature.OCEAN_MONUMENT.getName(), Collections.singletonList("monument")); + structureToFeature.put(Feature.END_CITY.getName(), Collections.singletonList("end_city")); + structureToFeature.put(Feature.OCEAN_RUIN.getName(), Arrays.asList("ocean_ruin", "ocean_ruin_warm", "ocean_ruin_small", "ocean_ruin_warm_small", "ocean_ruin_tall", "ocean_ruin_warm_tall")); + structureToFeature.put(Feature.VILLAGE.getName(), Arrays.asList("village", "village_desert", "village_savanna", "village_taiga", "village_snowy")); + structureToFeature.put(Feature.WOODLAND_MANSION.getName(), Collections.singletonList("mansion")); + structureToFeature.put(Feature.BURIED_TREASURE.getName(), Collections.singletonList("treasure")); + structureToFeature.put(Feature.SWAMP_HUT.getName(), Collections.singletonList("witch_hut")); + structureToFeature.put(Feature.STRONGHOLD.getName(), Collections.singletonList("stronghold")); + structureToFeature.put(Feature.DESERT_PYRAMID.getName(), Collections.singletonList("desert_temple")); + structureToFeature.put(Feature.JUNGLE_TEMPLE.getName(), Collections.singletonList("jungle_temple")); + structureToFeature.put(Feature.SHIPWRECK.getName(), Arrays.asList("shipwreck", "shipwreck2")); + structureToFeature.put(Feature.PILLAGER_OUTPOST.getName(), Collections.singletonList("pillager_outpost")); + structureToFeature.put(Feature.MINESHAFT.getName(), Arrays.asList("mineshaft", "mineshaft_mesa")); + structureToFeature.put(Feature.IGLOO.getName(), Collections.singletonList("igloo")); + structureToFeature.put(Feature.NETHER_BRIDGE.getName(), Collections.singletonList("fortress")); structureToFeature.forEach((key, value) -> value.forEach(el -> featureToStructure.put(el, key))); } diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index 85837f2d..8af5adf7 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -111,6 +111,8 @@ import net.minecraft.util.math.BlockBox; import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3i; import net.minecraft.util.registry.Registry; +import net.minecraft.world.gen.feature.Feature; +import net.minecraft.world.gen.feature.StructureFeature; import net.minecraft.world.poi.PointOfInterest; import net.minecraft.world.poi.PointOfInterestStorage; import net.minecraft.world.poi.PointOfInterestType; @@ -1119,6 +1121,56 @@ public class CarpetExpression return (_c, _t) -> ret; }); + this.expr.addLazyFunction("structure_eligibility", -1, (c, t, lv) -> + { + CarpetContext cc = (CarpetContext)c; + BlockArgument locator = BlockArgument.findIn(cc, lv, 0); + + ServerWorld world = cc.s.getWorld(); + BlockPos pos = locator.block.getPos(); + StructureFeature structure = null; + boolean needSize = false; + if (lv.size() > locator.offset) + { + Value requested = lv.get(locator.offset+0).evalValue(c); + if (!(requested instanceof NullValue)) + { + String reqString = requested.getString(); + String structureName = FeatureGenerator.featureToStructure.get(reqString); + if (structureName == null) throw new InternalExpressionException("Unknown structure: " + reqString); + structure = Feature.STRUCTURES.get(structureName); + } + if (lv.size() > locator.offset+1) + { + needSize = lv.get(locator.offset+1).evalValue(c).getBoolean(); + } + } + if (structure != null) + { + BlockBox box = FeatureGenerator.shouldStructureStartAt(world, pos, structure, needSize); + if (box == null) return LazyValue.NULL; + if (!needSize) return LazyValue.TRUE; + Value ret = ListValue.of( + ListValue.of(new NumericValue(box.minX), new NumericValue(box.minY), new NumericValue(box.minZ)), + ListValue.of(new NumericValue(box.minX), new NumericValue(box.minY), new NumericValue(box.minZ)) + ); + return (_c, _t) -> ret; + } + Map ret = new HashMap<>(); + for(StructureFeature str : Feature.STRUCTURES.values()) + { + BlockBox box = FeatureGenerator.shouldStructureStartAt(world, pos, str, needSize); + if (box == null) continue; + Value key = new StringValue(FeatureGenerator.structureToFeature.get(str.getName()).get(0)); + ret.put(key, (!needSize)?Value.NULL:ListValue.of( + ListValue.of(new NumericValue(box.minX), new NumericValue(box.minY), new NumericValue(box.minZ)), + ListValue.of(new NumericValue(box.minX), new NumericValue(box.minY), new NumericValue(box.minZ)) + )); + } + Value retMap = MapValue.wrap(ret); + return (_c, _t) -> retMap; + }); + this.expr.addLazyFunction("structures", -1, (c, t, lv) -> { CarpetContext cc = (CarpetContext)c; BlockArgument locator = BlockArgument.findIn(cc, lv, 0);