diff --git a/src/main/java/carpet/fakes/StructureFeatureInterface.java b/src/main/java/carpet/fakes/StructureFeatureInterface.java index 410f5055..4efea22e 100644 --- a/src/main/java/carpet/fakes/StructureFeatureInterface.java +++ b/src/main/java/carpet/fakes/StructureFeatureInterface.java @@ -8,5 +8,6 @@ import net.minecraft.world.gen.chunk.ChunkGeneratorConfig; public interface StructureFeatureInterface { boolean plopAnywhere(IWorld world, BlockPos pos); - boolean plopAnywhere(IWorld world, BlockPos pos, ChunkGenerator generator); + boolean gridAnywhere(IWorld world, BlockPos pos); + boolean plopAnywhere(IWorld world, BlockPos pos, ChunkGenerator generator, boolean wireOnly); } diff --git a/src/main/java/carpet/helpers/FeatureGenerator.java b/src/main/java/carpet/helpers/FeatureGenerator.java index 87c76e52..182472f8 100644 --- a/src/main/java/carpet/helpers/FeatureGenerator.java +++ b/src/main/java/carpet/helpers/FeatureGenerator.java @@ -32,7 +32,10 @@ import net.minecraft.world.gen.feature.ShipwreckFeatureConfig; import net.minecraft.world.gen.feature.StructureFeature; import net.minecraft.world.gen.feature.VillageFeatureConfig; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; public class FeatureGenerator @@ -60,6 +63,14 @@ public class FeatureGenerator private static Thing spawnCustomStructure(StructureFeature structure, FeatureConfig conf, Biome biome) { + return setupCustomStructure(structure, conf, biome, false); + } + private static Thing gridCustomStructure(StructureFeature structure, FeatureConfig conf, Biome biome) + { + return setupCustomStructure(structure, conf, biome, true); + } + private static Thing setupCustomStructure(StructureFeature structure, FeatureConfig conf, Biome biome, boolean wireOnly) + { return (w, p) -> { ChunkGenerator chunkgen = new OverworldChunkGenerator(w, w.getChunkManager().getChunkGenerator().getBiomeSource(), new OverworldChunkGeneratorConfig()) // BiomeSourceType.VANILLA_LAYERED.applyConfig((BiomeSourceType.VANILLA_LAYERED.getConfig())), ChunkGeneratorType.SURFACE.createSettings()) { @@ -84,7 +95,7 @@ public class FeatureGenerator }; - return ((StructureFeatureInterface)structure).plopAnywhere(w, p, chunkgen); + return ((StructureFeatureInterface)structure).plopAnywhere(w, p, chunkgen, wireOnly); }; } @@ -95,9 +106,79 @@ public class FeatureGenerator return null; } + public static Boolean gridStructure(String name, World world, BlockPos pos) + { + if (gridMap.containsKey(name)) + return gridMap.get(name).plop(world, pos); + return null; + } + + + public static final Map> structureToBuilding = new HashMap<>(); + public static final Map buildingToStructure = new HashMap<>(); + static + { + structureToBuilding.put("Monument", Collections.singletonList("monument")); + structureToBuilding.put("EndCity", Collections.singletonList("end_city")); + structureToBuilding.put("Ocean_Ruin", Arrays.asList("oceanruin", "oceanruin_warm", "oceanruin_small", "oceanruin_warm_small", "oceanruin_tall", "oceanruin_warm_tall")); + structureToBuilding.put("Village", Arrays.asList("village", "village_desert", "village_savanna", "village_taiga", "village_snowy")); + structureToBuilding.put("Mansion", Collections.singletonList("mansion")); + structureToBuilding.put("Buried_Treasure", Collections.singletonList("treasure")); + structureToBuilding.put("Swamp_Hut", Collections.singletonList("witchhut")); + structureToBuilding.put("Stronghold", Collections.singletonList("stronghold")); + structureToBuilding.put("Desert_Pyramid", Collections.singletonList("desert_temple")); + structureToBuilding.put("Jungle_Pyramid", Collections.singletonList("jungle_temple")); + structureToBuilding.put("Shipwreck", Arrays.asList("shipwreck", "shipwreck2")); + structureToBuilding.put("Pillager_Outpost", Collections.singletonList("pillager_outpost")); + structureToBuilding.put("Mineshaft", Arrays.asList("mineshaft", "mineshaft_mesa")); + structureToBuilding.put("Igloo", Collections.singletonList("igloo")); + + structureToBuilding.forEach((key, value) -> value.forEach(el -> buildingToStructure.put(el, key))); + } + + + private static final Map gridMap = new HashMap() {{ + put("monument", ((StructureFeatureInterface)Feature.OCEAN_MONUMENT)::gridAnywhere); + put("fortress", ((StructureFeatureInterface)Feature.NETHER_BRIDGE)::gridAnywhere); + put("mansion", ((StructureFeatureInterface)Feature.WOODLAND_MANSION)::gridAnywhere); + put("jungle_temple", ((StructureFeatureInterface)Feature.JUNGLE_TEMPLE)::gridAnywhere); + put("desert_temple", ((StructureFeatureInterface)Feature.DESERT_PYRAMID)::gridAnywhere); + put("end_city", ((StructureFeatureInterface)Feature.END_CITY)::gridAnywhere); + put("igloo", ((StructureFeatureInterface)Feature.IGLOO)::gridAnywhere); + put("shipwreck", gridCustomStructure(Feature.SHIPWRECK, new ShipwreckFeatureConfig(true), Biomes.PLAINS)); + put("shipwreck2", gridCustomStructure(Feature.SHIPWRECK, new ShipwreckFeatureConfig(false), Biomes.PLAINS)); + put("witchhut", ((StructureFeatureInterface)Feature.SWAMP_HUT)::gridAnywhere); + put("stronghold", ((StructureFeatureInterface)Feature.STRONGHOLD)::gridAnywhere); + + put("oceanruin_small", gridCustomStructure(Feature.OCEAN_RUIN, + new OceanRuinFeatureConfig(OceanRuinFeature.BiomeType.COLD, 0.0F, 0.5F), Biomes.PLAINS)); + put("oceanruin_warm_small", gridCustomStructure(Feature.OCEAN_RUIN, + new OceanRuinFeatureConfig(OceanRuinFeature.BiomeType.WARM, 0.0F, 0.5F), Biomes.PLAINS)); + put("oceanruin_tall", gridCustomStructure(Feature.OCEAN_RUIN, + new OceanRuinFeatureConfig(OceanRuinFeature.BiomeType.COLD, 1.0F, 0.0F), Biomes.PLAINS)); + put("oceanruin_warm_tall", gridCustomStructure(Feature.OCEAN_RUIN, + new OceanRuinFeatureConfig(OceanRuinFeature.BiomeType.WARM, 1.0F, 0.0F), Biomes.PLAINS)); + put("oceanruin", gridCustomStructure(Feature.OCEAN_RUIN, + new OceanRuinFeatureConfig(OceanRuinFeature.BiomeType.COLD, 1.0F, 1.0F), Biomes.PLAINS)); + put("oceanruin_warm", gridCustomStructure(Feature.OCEAN_RUIN, + new OceanRuinFeatureConfig(OceanRuinFeature.BiomeType.WARM, 1.0F, 1.0F), Biomes.PLAINS)); + put("treasure", ((StructureFeatureInterface)Feature.BURIED_TREASURE)::gridAnywhere); + + put("pillager_outpost", ((StructureFeatureInterface)Feature.PILLAGER_OUTPOST)::gridAnywhere); + + + put("mineshaft", gridCustomStructure(Feature.MINESHAFT, new MineshaftFeatureConfig(0.0, MineshaftFeature.Type.NORMAL), Biomes.PLAINS)); + put("mineshaft_mesa", gridCustomStructure(Feature.MINESHAFT, new MineshaftFeatureConfig(0.0, MineshaftFeature.Type.MESA), Biomes.PLAINS)); + + put("village", gridCustomStructure(Feature.VILLAGE, new VillageFeatureConfig("village/plains/town_centers",6), Biomes.PLAINS)); + put("village_desert", gridCustomStructure(Feature.VILLAGE, new VillageFeatureConfig("village/desert/town_centers", 6), Biomes.PLAINS)); + put("village_savanna", gridCustomStructure(Feature.VILLAGE, new VillageFeatureConfig("village/savanna/town_centers", 6), Biomes.PLAINS)); + put("village_taiga", gridCustomStructure(Feature.VILLAGE, new VillageFeatureConfig("village/taiga/town_centers", 6), Biomes.PLAINS)); + put("village_snowy", gridCustomStructure(Feature.VILLAGE, new VillageFeatureConfig("village/snowy/town_centers", 6), Biomes.PLAINS)); + + }}; + private static final Map featureMap = new HashMap() {{ - - put("oak", simplePlop(Feature.NORMAL_TREE)); put("oak_large", simplePlop(Feature.FANCY_TREE)); put("birch", simplePlop(Feature.BIRCH_TREE)); diff --git a/src/main/java/carpet/mixins/StructureFeatureMixin.java b/src/main/java/carpet/mixins/StructureFeatureMixin.java index cac476f4..0158fc1f 100644 --- a/src/main/java/carpet/mixins/StructureFeatureMixin.java +++ b/src/main/java/carpet/mixins/StructureFeatureMixin.java @@ -34,9 +34,15 @@ public abstract class StructureFeatureMixin implements StructureFeatureInterface @Override public boolean plopAnywhere(IWorld world, BlockPos pos) { - return plopAnywhere(world, pos, world.getChunkManager().getChunkGenerator()); + return plopAnywhere(world, pos, world.getChunkManager().getChunkGenerator(), false); } - public boolean plopAnywhere(IWorld world, BlockPos pos, ChunkGenerator generator) + @Override + public boolean gridAnywhere(IWorld world, BlockPos pos) + { + return plopAnywhere(world, pos, world.getChunkManager().getChunkGenerator(), true); + } + + public boolean plopAnywhere(IWorld world, BlockPos pos, ChunkGenerator generator, boolean wireOnly) { if (world.isClient()) return false; @@ -48,7 +54,7 @@ public abstract class StructureFeatureMixin implements StructureFeatureInterface int k = pos.getZ() >> 4; long chId = ChunkPos.toLong(j, k); StructureStart structurestart = forceStructureStart(world, generator, rand, chId); - if (structurestart == null || structurestart == StructureStart.DEFAULT) + if (structurestart == StructureStart.DEFAULT) { CarpetSettings.skipGenerationChecks = false; return false; @@ -58,16 +64,19 @@ public abstract class StructureFeatureMixin implements StructureFeatureInterface world.getChunk(j, k).addStructureReference(this.getName(), chId); //, ChunkStatus.STRUCTURE_STARTS MutableIntBoundingBox box = structurestart.getBoundingBox(); - structurestart.generateStructure( - world, - rand, - new MutableIntBoundingBox( - pos.getX() - this.getRadius()*16, - pos.getZ() - this.getRadius()*16, - pos.getX() + (this.getRadius()+1)*16, - pos.getZ() + (1+this.getRadius())*16), - new ChunkPos(j, k) - ); + if (!wireOnly) + { + structurestart.generateStructure( + world, + rand, + new MutableIntBoundingBox( + pos.getX() - this.getRadius() * 16, + pos.getZ() - this.getRadius() * 16, + pos.getX() + (this.getRadius() + 1) * 16, + pos.getZ() + (1 + this.getRadius()) * 16), + new ChunkPos(j, k) + ); + } //structurestart.notifyPostProcessAt(new ChunkPos(j, k)); int i = getRadius(); @@ -93,7 +102,10 @@ public abstract class StructureFeatureMixin implements StructureFeatureInterface CarpetSettings.skipGenerationChecks = false; return false; } - CarpetSettings.skipGenerationChecks = false; + finally + { + CarpetSettings.skipGenerationChecks = false; + } return true; } diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index e7e24fc9..c609c992 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -646,7 +646,7 @@ public class CarpetExpression * and structure_starts. Returns null if the chunk is not in memory unless called with optional * true.

*

structures(pos), structures(pos, structure_name)

- *

Returns structure information for a given block position. Note that strucuture information is the same for + *

Returns structure information for a given block position. Note that structure information is the same for * all the blocks from the same chunk. structures function can be called with a block, or a block and * a structure name. In the first case it returns a map of structures at a given position, keyed by structure name, * with values indicating the bounding box of the structure - a pair of two 3-value coords (see examples). When @@ -658,6 +658,11 @@ public class CarpetExpression * a structure name. In the first case it returns a list of structure names that give chunk belongs to. When * called with an extra structure name, returns list of positions pointing to the lowest block position in chunks that * hold structure starts for these structures. You can query that chunk structures then to get its bounding boxes.

+ *

structure_set(pos, structure_name), structure_set(pos, structure_name, null)

+ *

Creates or removes structure information of a structure associated with a chunk of pos. + * Unlike plop, blocks are not placed in the world, only structure information is set. For the game this is a fully + * functional structure even if blocks are not set.. To remove structure a given point is in, + * use structure_references to find where current structure starts.

*

suffocates(pos)

*

Boolean function, true if the block causes suffocation.

@@ -899,7 +904,11 @@ public class CarpetExpression PointOfInterestStorage store = cc.s.getWorld().getPointOfInterestStorage(); if (poi == Value.NULL) { // clear poi information - if (store.getType(pos).isPresent()) store.remove(pos); + if (store.getType(pos).isPresent()) + { + store.remove(pos); + return LazyValue.TRUE; + } return LazyValue.NULL; } String poiTypeString = poi.getString().toLowerCase(Locale.ROOT); @@ -926,7 +935,7 @@ public class CarpetExpression for (int i=0; i < finalO; i++) ((PointOfInterest_scarpetMixin)p).callReserveTicket(); }); } - return LazyValue.NULL; + return LazyValue.TRUE; }); @@ -1396,10 +1405,11 @@ public class CarpetExpression { List referenceList = references.entrySet().stream(). filter(e -> e.getValue()!= null && !e.getValue().isEmpty()). - map(e -> new StringValue(e.getKey())).collect(Collectors.toList()); + map(e -> new StringValue(FeatureGenerator.structureToBuilding.get(e.getKey()).get(0))).collect(Collectors.toList()); return (_c, _t ) -> ListValue.wrap(referenceList); } - String structureName = lv.get(locator.offset).evalValue(c).getString(); + String simpleStructureName = lv.get(locator.offset).evalValue(c).getString().toLowerCase(Locale.ROOT); + String structureName = FeatureGenerator.buildingToStructure.get(simpleStructureName); LongSet structureReferences = references.get(structureName); if (structureReferences == null || structureReferences.isEmpty()) { @@ -1422,7 +1432,7 @@ public class CarpetExpression Map structures = world.getChunk(pos).getStructureStarts(); if (lv.size() == locator.offset) { - Map strucutureList = new HashMap<>(); + Map structureList = new HashMap<>(); for (Map.Entry entry : structures.entrySet()) { StructureStart start = entry.getValue(); @@ -1431,13 +1441,13 @@ public class CarpetExpression MutableIntBoundingBox box = start.getBoundingBox(); ListValue coord1 = ListValue.of(new NumericValue(box.minX), new NumericValue(box.minY), new NumericValue(box.minZ)); ListValue coord2 = ListValue.of(new NumericValue(box.maxX), new NumericValue(box.maxY), new NumericValue(box.maxZ)); - strucutureList.put(new StringValue(entry.getKey()), ListValue.of(coord1, coord2)); + structureList.put(new StringValue(FeatureGenerator.structureToBuilding.get(entry.getKey()).get(0)), ListValue.of(coord1, coord2)); } - Value ret = MapValue.wrap(strucutureList); + Value ret = MapValue.wrap(structureList); return (_c, _t) -> ret; } - String structureName = lv.get(locator.offset).evalValue(c).getString(); - StructureStart start = structures.get(structureName); + String structureName = lv.get(locator.offset).evalValue(c).getString().toLowerCase(Locale.ROOT); + StructureStart start = structures.get(FeatureGenerator.buildingToStructure.get(structureName)); if (start == null || start == StructureStart.DEFAULT) return (_c, _t) -> Value.NULL; List pieces = new ArrayList<>(); @@ -1454,8 +1464,61 @@ public class CarpetExpression Value ret = ListValue.wrap(pieces); return (_c, _t) -> ret; }); - // need get_biome + this.expr.addLazyFunction("structure_set", -1, (c, t, lv) -> + { + CarpetContext cc = (CarpetContext)c; + BlockValue.LocatorResult locator = BlockValue.fromParams(cc, lv, 0); + + ServerWorld world = cc.s.getWorld(); + BlockPos pos = locator.block.getPos(); + + if (lv.size() == locator.offset) + throw new InternalExpressionException("'structure_set requires at least position and a structure name"); + String simpleStructureName = lv.get(locator.offset).evalValue(c).getString().toLowerCase(Locale.ROOT); + String structureName = FeatureGenerator.buildingToStructure.get(simpleStructureName); + + Value[] result = new Value[]{Value.NULL}; + ((CarpetContext) c).s.getMinecraftServer().executeSync(() -> + { + Map structures = world.getChunk(pos).getStructureStarts(); + if (lv.size() == locator.offset + 1) + { + //if (newValue.getString().equalsIgnoreCase("default")) + // spawn default structure for location + Boolean res = FeatureGenerator.gridStructure(simpleStructureName, ((CarpetContext) c).s.getWorld(), locator.block.getPos()); + if (res == null) return; + result[0] = res?Value.TRUE:Value.FALSE; + return; + } + Value newValue = lv.get(locator.offset+1).evalValue(c); + if (newValue instanceof NullValue) // remove structure + { + if (!structures.containsKey(structureName)) + { + return; + } + StructureStart start = structures.get(structureName); + ChunkPos structureChunkPos = new ChunkPos(start.getChunkX(), start.getChunkZ()); + MutableIntBoundingBox box = start.getBoundingBox(); + for (int chx = box.minX / 16; chx <= box.maxX / 16; chx++) + { + for (int chz = box.minZ / 16; chz <= box.maxZ / 16; chz++) + { + ChunkPos chpos = new ChunkPos(chx, chz); + // getting a chunk will convert it to full, allowing to modify references + Map references = world.getChunk(chpos.getCenterBlockPos()).getStructureReferences(); + if (references.containsKey(structureName) && references.get(structureName) != null) + references.get(structureName).remove(structureChunkPos.toLong()); + } + } + structures.remove(structureName); + result[0] = Value.TRUE; + } + }); + Value ret = result[0]; // preventing from lazy evaluating of the result in case a future completes later + return (_c, _t) -> ret; + }); } /**