package net.minecraft.world.level.levelgen.structure.structures; import com.google.common.collect.Lists; import java.util.List; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.BlockTags; import net.minecraft.tags.FluidTags; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.util.valueproviders.ConstantInt; import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.monster.Drowned; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ServerLevelAccessor; import net.minecraft.world.level.StructureManager; import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.ChestBlock; import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.ChestBlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.StructurePieceAccessor; import net.minecraft.world.level.levelgen.structure.TemplateStructurePiece; import net.minecraft.world.level.levelgen.structure.pieces.StructurePieceSerializationContext; import net.minecraft.world.level.levelgen.structure.pieces.StructurePieceType; import net.minecraft.world.level.levelgen.structure.templatesystem.AlwaysTrueTest; import net.minecraft.world.level.levelgen.structure.templatesystem.BlockIgnoreProcessor; import net.minecraft.world.level.levelgen.structure.templatesystem.BlockMatchTest; import net.minecraft.world.level.levelgen.structure.templatesystem.BlockRotProcessor; import net.minecraft.world.level.levelgen.structure.templatesystem.CappedProcessor; import net.minecraft.world.level.levelgen.structure.templatesystem.PosAlwaysTrueTest; import net.minecraft.world.level.levelgen.structure.templatesystem.ProcessorRule; import net.minecraft.world.level.levelgen.structure.templatesystem.RuleProcessor; import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; import net.minecraft.world.level.levelgen.structure.templatesystem.rule.blockentity.AppendLoot; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.storage.loot.BuiltInLootTables; import net.minecraft.world.level.storage.loot.LootTable; public class OceanRuinPieces { static final StructureProcessor WARM_SUSPICIOUS_BLOCK_PROCESSOR = archyRuleProcessor( Blocks.SAND, Blocks.SUSPICIOUS_SAND, BuiltInLootTables.OCEAN_RUIN_WARM_ARCHAEOLOGY ); static final StructureProcessor COLD_SUSPICIOUS_BLOCK_PROCESSOR = archyRuleProcessor( Blocks.GRAVEL, Blocks.SUSPICIOUS_GRAVEL, BuiltInLootTables.OCEAN_RUIN_COLD_ARCHAEOLOGY ); private static final ResourceLocation[] WARM_RUINS = new ResourceLocation[]{ ResourceLocation.withDefaultNamespace("underwater_ruin/warm_1"), ResourceLocation.withDefaultNamespace("underwater_ruin/warm_2"), ResourceLocation.withDefaultNamespace("underwater_ruin/warm_3"), ResourceLocation.withDefaultNamespace("underwater_ruin/warm_4"), ResourceLocation.withDefaultNamespace("underwater_ruin/warm_5"), ResourceLocation.withDefaultNamespace("underwater_ruin/warm_6"), ResourceLocation.withDefaultNamespace("underwater_ruin/warm_7"), ResourceLocation.withDefaultNamespace("underwater_ruin/warm_8") }; private static final ResourceLocation[] RUINS_BRICK = new ResourceLocation[]{ ResourceLocation.withDefaultNamespace("underwater_ruin/brick_1"), ResourceLocation.withDefaultNamespace("underwater_ruin/brick_2"), ResourceLocation.withDefaultNamespace("underwater_ruin/brick_3"), ResourceLocation.withDefaultNamespace("underwater_ruin/brick_4"), ResourceLocation.withDefaultNamespace("underwater_ruin/brick_5"), ResourceLocation.withDefaultNamespace("underwater_ruin/brick_6"), ResourceLocation.withDefaultNamespace("underwater_ruin/brick_7"), ResourceLocation.withDefaultNamespace("underwater_ruin/brick_8") }; private static final ResourceLocation[] RUINS_CRACKED = new ResourceLocation[]{ ResourceLocation.withDefaultNamespace("underwater_ruin/cracked_1"), ResourceLocation.withDefaultNamespace("underwater_ruin/cracked_2"), ResourceLocation.withDefaultNamespace("underwater_ruin/cracked_3"), ResourceLocation.withDefaultNamespace("underwater_ruin/cracked_4"), ResourceLocation.withDefaultNamespace("underwater_ruin/cracked_5"), ResourceLocation.withDefaultNamespace("underwater_ruin/cracked_6"), ResourceLocation.withDefaultNamespace("underwater_ruin/cracked_7"), ResourceLocation.withDefaultNamespace("underwater_ruin/cracked_8") }; private static final ResourceLocation[] RUINS_MOSSY = new ResourceLocation[]{ ResourceLocation.withDefaultNamespace("underwater_ruin/mossy_1"), ResourceLocation.withDefaultNamespace("underwater_ruin/mossy_2"), ResourceLocation.withDefaultNamespace("underwater_ruin/mossy_3"), ResourceLocation.withDefaultNamespace("underwater_ruin/mossy_4"), ResourceLocation.withDefaultNamespace("underwater_ruin/mossy_5"), ResourceLocation.withDefaultNamespace("underwater_ruin/mossy_6"), ResourceLocation.withDefaultNamespace("underwater_ruin/mossy_7"), ResourceLocation.withDefaultNamespace("underwater_ruin/mossy_8") }; private static final ResourceLocation[] BIG_RUINS_BRICK = new ResourceLocation[]{ ResourceLocation.withDefaultNamespace("underwater_ruin/big_brick_1"), ResourceLocation.withDefaultNamespace("underwater_ruin/big_brick_2"), ResourceLocation.withDefaultNamespace("underwater_ruin/big_brick_3"), ResourceLocation.withDefaultNamespace("underwater_ruin/big_brick_8") }; private static final ResourceLocation[] BIG_RUINS_MOSSY = new ResourceLocation[]{ ResourceLocation.withDefaultNamespace("underwater_ruin/big_mossy_1"), ResourceLocation.withDefaultNamespace("underwater_ruin/big_mossy_2"), ResourceLocation.withDefaultNamespace("underwater_ruin/big_mossy_3"), ResourceLocation.withDefaultNamespace("underwater_ruin/big_mossy_8") }; private static final ResourceLocation[] BIG_RUINS_CRACKED = new ResourceLocation[]{ ResourceLocation.withDefaultNamespace("underwater_ruin/big_cracked_1"), ResourceLocation.withDefaultNamespace("underwater_ruin/big_cracked_2"), ResourceLocation.withDefaultNamespace("underwater_ruin/big_cracked_3"), ResourceLocation.withDefaultNamespace("underwater_ruin/big_cracked_8") }; private static final ResourceLocation[] BIG_WARM_RUINS = new ResourceLocation[]{ ResourceLocation.withDefaultNamespace("underwater_ruin/big_warm_4"), ResourceLocation.withDefaultNamespace("underwater_ruin/big_warm_5"), ResourceLocation.withDefaultNamespace("underwater_ruin/big_warm_6"), ResourceLocation.withDefaultNamespace("underwater_ruin/big_warm_7") }; private static StructureProcessor archyRuleProcessor(Block block, Block suspiciousBlock, ResourceKey lootTable) { return new CappedProcessor( new RuleProcessor( List.of( new ProcessorRule( new BlockMatchTest(block), AlwaysTrueTest.INSTANCE, PosAlwaysTrueTest.INSTANCE, suspiciousBlock.defaultBlockState(), new AppendLoot(lootTable) ) ) ), ConstantInt.of(5) ); } private static ResourceLocation getSmallWarmRuin(RandomSource random) { return Util.getRandom(WARM_RUINS, random); } private static ResourceLocation getBigWarmRuin(RandomSource random) { return Util.getRandom(BIG_WARM_RUINS, random); } public static void addPieces( StructureTemplateManager structureTemplateManager, BlockPos pos, Rotation rotation, StructurePieceAccessor structurePieceAccessor, RandomSource random, OceanRuinStructure structure ) { boolean bl = random.nextFloat() <= structure.largeProbability; float f = bl ? 0.9F : 0.8F; addPiece(structureTemplateManager, pos, rotation, structurePieceAccessor, random, structure, bl, f); if (bl && random.nextFloat() <= structure.clusterProbability) { addClusterRuins(structureTemplateManager, random, rotation, pos, structure, structurePieceAccessor); } } private static void addClusterRuins( StructureTemplateManager structureTemplateManager, RandomSource random, Rotation rotation, BlockPos pos, OceanRuinStructure structure, StructurePieceAccessor structurePieceAccessor ) { BlockPos blockPos = new BlockPos(pos.getX(), 90, pos.getZ()); BlockPos blockPos2 = StructureTemplate.transform(new BlockPos(15, 0, 15), Mirror.NONE, rotation, BlockPos.ZERO).offset(blockPos); BoundingBox boundingBox = BoundingBox.fromCorners(blockPos, blockPos2); BlockPos blockPos3 = new BlockPos(Math.min(blockPos.getX(), blockPos2.getX()), blockPos.getY(), Math.min(blockPos.getZ(), blockPos2.getZ())); List list = allPositions(random, blockPos3); int i = Mth.nextInt(random, 4, 8); for (int j = 0; j < i; j++) { if (!list.isEmpty()) { int k = random.nextInt(list.size()); BlockPos blockPos4 = (BlockPos)list.remove(k); Rotation rotation2 = Rotation.getRandom(random); BlockPos blockPos5 = StructureTemplate.transform(new BlockPos(5, 0, 6), Mirror.NONE, rotation2, BlockPos.ZERO).offset(blockPos4); BoundingBox boundingBox2 = BoundingBox.fromCorners(blockPos4, blockPos5); if (!boundingBox2.intersects(boundingBox)) { addPiece(structureTemplateManager, blockPos4, rotation2, structurePieceAccessor, random, structure, false, 0.8F); } } } } private static List allPositions(RandomSource random, BlockPos pos) { List list = Lists.newArrayList(); list.add(pos.offset(-16 + Mth.nextInt(random, 1, 8), 0, 16 + Mth.nextInt(random, 1, 7))); list.add(pos.offset(-16 + Mth.nextInt(random, 1, 8), 0, Mth.nextInt(random, 1, 7))); list.add(pos.offset(-16 + Mth.nextInt(random, 1, 8), 0, -16 + Mth.nextInt(random, 4, 8))); list.add(pos.offset(Mth.nextInt(random, 1, 7), 0, 16 + Mth.nextInt(random, 1, 7))); list.add(pos.offset(Mth.nextInt(random, 1, 7), 0, -16 + Mth.nextInt(random, 4, 6))); list.add(pos.offset(16 + Mth.nextInt(random, 1, 7), 0, 16 + Mth.nextInt(random, 3, 8))); list.add(pos.offset(16 + Mth.nextInt(random, 1, 7), 0, Mth.nextInt(random, 1, 7))); list.add(pos.offset(16 + Mth.nextInt(random, 1, 7), 0, -16 + Mth.nextInt(random, 4, 8))); return list; } private static void addPiece( StructureTemplateManager structureTemplateManager, BlockPos pos, Rotation rotation, StructurePieceAccessor structurePieceAccessor, RandomSource random, OceanRuinStructure structure, boolean isLarge, float integrity ) { switch (structure.biomeTemp) { case WARM: default: ResourceLocation resourceLocation = isLarge ? getBigWarmRuin(random) : getSmallWarmRuin(random); structurePieceAccessor.addPiece( new OceanRuinPieces.OceanRuinPiece(structureTemplateManager, resourceLocation, pos, rotation, integrity, structure.biomeTemp, isLarge) ); break; case COLD: ResourceLocation[] resourceLocations = isLarge ? BIG_RUINS_BRICK : RUINS_BRICK; ResourceLocation[] resourceLocations2 = isLarge ? BIG_RUINS_CRACKED : RUINS_CRACKED; ResourceLocation[] resourceLocations3 = isLarge ? BIG_RUINS_MOSSY : RUINS_MOSSY; int i = random.nextInt(resourceLocations.length); structurePieceAccessor.addPiece( new OceanRuinPieces.OceanRuinPiece(structureTemplateManager, resourceLocations[i], pos, rotation, integrity, structure.biomeTemp, isLarge) ); structurePieceAccessor.addPiece( new OceanRuinPieces.OceanRuinPiece(structureTemplateManager, resourceLocations2[i], pos, rotation, 0.7F, structure.biomeTemp, isLarge) ); structurePieceAccessor.addPiece( new OceanRuinPieces.OceanRuinPiece(structureTemplateManager, resourceLocations3[i], pos, rotation, 0.5F, structure.biomeTemp, isLarge) ); } } public static class OceanRuinPiece extends TemplateStructurePiece { private final OceanRuinStructure.Type biomeType; private final float integrity; private final boolean isLarge; public OceanRuinPiece( StructureTemplateManager structureTemplateManager, ResourceLocation location, BlockPos pos, Rotation rotation, float integrity, OceanRuinStructure.Type biomeType, boolean isLarge ) { super(StructurePieceType.OCEAN_RUIN, 0, structureTemplateManager, location, location.toString(), makeSettings(rotation, integrity, biomeType), pos); this.integrity = integrity; this.biomeType = biomeType; this.isLarge = isLarge; } private OceanRuinPiece( StructureTemplateManager structureTemplateManager, CompoundTag genDepth, Rotation rotation, float integrity, OceanRuinStructure.Type biomeType, boolean isLarge ) { super(StructurePieceType.OCEAN_RUIN, genDepth, structureTemplateManager, resourceLocation -> makeSettings(rotation, integrity, biomeType)); this.integrity = integrity; this.biomeType = biomeType; this.isLarge = isLarge; } private static StructurePlaceSettings makeSettings(Rotation rotation, float integrity, OceanRuinStructure.Type structureType) { StructureProcessor structureProcessor = structureType == OceanRuinStructure.Type.COLD ? OceanRuinPieces.COLD_SUSPICIOUS_BLOCK_PROCESSOR : OceanRuinPieces.WARM_SUSPICIOUS_BLOCK_PROCESSOR; return new StructurePlaceSettings() .setRotation(rotation) .setMirror(Mirror.NONE) .addProcessor(new BlockRotProcessor(integrity)) .addProcessor(BlockIgnoreProcessor.STRUCTURE_AND_AIR) .addProcessor(structureProcessor); } public static OceanRuinPieces.OceanRuinPiece create(StructureTemplateManager structureTemplateManager, CompoundTag tag) { Rotation rotation = (Rotation)tag.read("Rot", Rotation.LEGACY_CODEC).orElseThrow(); float f = tag.getFloatOr("Integrity", 0.0F); OceanRuinStructure.Type type = (OceanRuinStructure.Type)tag.read("BiomeType", OceanRuinStructure.Type.LEGACY_CODEC).orElseThrow(); boolean bl = tag.getBooleanOr("IsLarge", false); return new OceanRuinPieces.OceanRuinPiece(structureTemplateManager, tag, rotation, f, type, bl); } @Override protected void addAdditionalSaveData(StructurePieceSerializationContext context, CompoundTag tag) { super.addAdditionalSaveData(context, tag); tag.store("Rot", Rotation.LEGACY_CODEC, this.placeSettings.getRotation()); tag.putFloat("Integrity", this.integrity); tag.store("BiomeType", OceanRuinStructure.Type.LEGACY_CODEC, this.biomeType); tag.putBoolean("IsLarge", this.isLarge); } @Override protected void handleDataMarker(String name, BlockPos pos, ServerLevelAccessor level, RandomSource random, BoundingBox box) { if ("chest".equals(name)) { level.setBlock(pos, Blocks.CHEST.defaultBlockState().setValue(ChestBlock.WATERLOGGED, level.getFluidState(pos).is(FluidTags.WATER)), 2); BlockEntity blockEntity = level.getBlockEntity(pos); if (blockEntity instanceof ChestBlockEntity) { ((ChestBlockEntity)blockEntity) .setLootTable(this.isLarge ? BuiltInLootTables.UNDERWATER_RUIN_BIG : BuiltInLootTables.UNDERWATER_RUIN_SMALL, random.nextLong()); } } else if ("drowned".equals(name)) { Drowned drowned = EntityType.DROWNED.create(level.getLevel(), EntitySpawnReason.STRUCTURE); if (drowned != null) { drowned.setPersistenceRequired(); drowned.snapTo(pos, 0.0F, 0.0F); drowned.finalizeSpawn(level, level.getCurrentDifficultyAt(pos), EntitySpawnReason.STRUCTURE, null); level.addFreshEntityWithPassengers(drowned); if (pos.getY() > level.getSeaLevel()) { level.setBlock(pos, Blocks.AIR.defaultBlockState(), 2); } else { level.setBlock(pos, Blocks.WATER.defaultBlockState(), 2); } } } } @Override public void postProcess( WorldGenLevel level, StructureManager structureManager, ChunkGenerator generator, RandomSource random, BoundingBox box, ChunkPos chunkPos, BlockPos pos ) { int i = level.getHeight(Heightmap.Types.OCEAN_FLOOR_WG, this.templatePosition.getX(), this.templatePosition.getZ()); this.templatePosition = new BlockPos(this.templatePosition.getX(), i, this.templatePosition.getZ()); BlockPos blockPos = StructureTemplate.transform( new BlockPos(this.template.getSize().getX() - 1, 0, this.template.getSize().getZ() - 1), Mirror.NONE, this.placeSettings.getRotation(), BlockPos.ZERO ) .offset(this.templatePosition); this.templatePosition = new BlockPos(this.templatePosition.getX(), this.getHeight(this.templatePosition, level, blockPos), this.templatePosition.getZ()); super.postProcess(level, structureManager, generator, random, box, chunkPos, pos); } private int getHeight(BlockPos templatePos, BlockGetter level, BlockPos pos) { int i = templatePos.getY(); int j = 512; int k = i - 1; int l = 0; for (BlockPos blockPos : BlockPos.betweenClosed(templatePos, pos)) { int m = blockPos.getX(); int n = blockPos.getZ(); int o = templatePos.getY() - 1; BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(m, o, n); BlockState blockState = level.getBlockState(mutableBlockPos); for (FluidState fluidState = level.getFluidState(mutableBlockPos); (blockState.isAir() || fluidState.is(FluidTags.WATER) || blockState.is(BlockTags.ICE)) && o > level.getMinY() + 1; fluidState = level.getFluidState(mutableBlockPos) ) { mutableBlockPos.set(m, --o, n); blockState = level.getBlockState(mutableBlockPos); } j = Math.min(j, o); if (o < k - 2) { l++; } } int p = Math.abs(templatePos.getX() - pos.getX()); if (k - j > 2 && l > p - 2) { i = j + 1; } return i; } } }