From bbd5a78a7f99544e21b1f65c0fe0520daf2c083a Mon Sep 17 00:00:00 2001 From: lordlogo2002 Date: Tue, 16 Dec 2025 05:43:30 +0100 Subject: [PATCH] broken room generator --- .../java/net/Chipperfluff/chipi/ChipiMod.java | 61 ++++++----- .../chipi/block/ChipperFrameBlock.java | 54 +++++++++ .../chipi/block/ChipperPortalBlock.java | 83 ++++++++++++++ .../chipi/block/ChipperPortalShape.java | 103 ++++++++++++++++++ .../Chipperfluff/chipi/block/ModBlocks.java | 38 ++++++- .../Chipperfluff/chipi/block/VoidBlock.java | 28 +++++ .../chipi/client/ChipiClient.java | 12 +- .../chipi/command/ChipperCommand.java | 23 ---- .../chipi/command/ChpCommand.java | 49 +++++++++ .../net/Chipperfluff/chipi/item/ModItems.java | 10 ++ .../world/gen/ChipiDungeonGenerator.java | 45 ++++++++ .../chipi/world/gen/ChipiStructure.java | 43 ++++++++ .../world/gen/struct/ChipiStructures.java | 21 ++++ .../world/gen/struct/CorridorEWStructure.java | 25 +++++ .../world/gen/struct/CorridorNSStructure.java | 25 +++++ .../world/gen/struct/RoomBaseStructure.java | 25 +++++ .../world/gen/struct/SpawnStructure.java | 25 +++++ .../chipi/blockstates/chipper_frame.json | 5 + .../chipi/blockstates/chipper_portal.json | 7 ++ .../chipi/models/block/chipper_frame.json | 6 + .../chipi/models/block/chipper_portal.json | 6 + .../chipi/models/item/chipper_frame.json | 3 + .../chipi/models/item/chipper_portal.json | 3 + .../chipi/textures/block/chipper_frame.png | Bin 0 -> 1294 bytes .../chipi/textures/block/chipper_portal.png | Bin 0 -> 1146 bytes .../chipi/textures/item/chipper_frame.png | Bin 0 -> 1294 bytes .../chipi/textures/item/chipper_portal.png | Bin 0 -> 1146 bytes .../chipi/structures/coridor_straight_ew.nbt | Bin 0 -> 1049 bytes .../chipi/structures/coridor_straight_ns.nbt | Bin 0 -> 1214 bytes .../data/chipi/structures/room_base.nbt | Bin 0 -> 7380 bytes 30 files changed, 640 insertions(+), 60 deletions(-) create mode 100644 src/main/java/net/Chipperfluff/chipi/block/ChipperFrameBlock.java create mode 100644 src/main/java/net/Chipperfluff/chipi/block/ChipperPortalBlock.java create mode 100644 src/main/java/net/Chipperfluff/chipi/block/ChipperPortalShape.java create mode 100644 src/main/java/net/Chipperfluff/chipi/block/VoidBlock.java delete mode 100644 src/main/java/net/Chipperfluff/chipi/command/ChipperCommand.java create mode 100644 src/main/java/net/Chipperfluff/chipi/command/ChpCommand.java create mode 100644 src/main/java/net/Chipperfluff/chipi/world/gen/ChipiDungeonGenerator.java create mode 100644 src/main/java/net/Chipperfluff/chipi/world/gen/ChipiStructure.java create mode 100644 src/main/java/net/Chipperfluff/chipi/world/gen/struct/ChipiStructures.java create mode 100644 src/main/java/net/Chipperfluff/chipi/world/gen/struct/CorridorEWStructure.java create mode 100644 src/main/java/net/Chipperfluff/chipi/world/gen/struct/CorridorNSStructure.java create mode 100644 src/main/java/net/Chipperfluff/chipi/world/gen/struct/RoomBaseStructure.java create mode 100644 src/main/java/net/Chipperfluff/chipi/world/gen/struct/SpawnStructure.java create mode 100644 src/main/resources/assets/chipi/blockstates/chipper_frame.json create mode 100644 src/main/resources/assets/chipi/blockstates/chipper_portal.json create mode 100644 src/main/resources/assets/chipi/models/block/chipper_frame.json create mode 100644 src/main/resources/assets/chipi/models/block/chipper_portal.json create mode 100644 src/main/resources/assets/chipi/models/item/chipper_frame.json create mode 100644 src/main/resources/assets/chipi/models/item/chipper_portal.json create mode 100644 src/main/resources/assets/chipi/textures/block/chipper_frame.png create mode 100644 src/main/resources/assets/chipi/textures/block/chipper_portal.png create mode 100644 src/main/resources/assets/chipi/textures/item/chipper_frame.png create mode 100644 src/main/resources/assets/chipi/textures/item/chipper_portal.png create mode 100644 src/main/resources/data/chipi/structures/coridor_straight_ew.nbt create mode 100644 src/main/resources/data/chipi/structures/coridor_straight_ns.nbt create mode 100644 src/main/resources/data/chipi/structures/room_base.nbt diff --git a/src/main/java/net/Chipperfluff/chipi/ChipiMod.java b/src/main/java/net/Chipperfluff/chipi/ChipiMod.java index 3c11c21..04297c4 100644 --- a/src/main/java/net/Chipperfluff/chipi/ChipiMod.java +++ b/src/main/java/net/Chipperfluff/chipi/ChipiMod.java @@ -1,17 +1,20 @@ package net.Chipperfluff.chipi; +import net.Chipperfluff.chipi.command.ChpCommand; +import net.Chipperfluff.chipi.item.ModItems; +import net.Chipperfluff.chipi.block.ModBlocks; +import net.Chipperfluff.chipi.world.gen.ChipiDungeonGenerator; + import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; -import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents; + import net.minecraft.server.world.ServerWorld; +import net.minecraft.structure.StructurePlacementData; +import net.minecraft.structure.StructureTemplate; import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; import net.minecraft.world.GameRules; -import net.minecraft.structure.StructurePlacementData; -import net.minecraft.structure.StructureTemplate; -import net.Chipperfluff.chipi.command.ChipperCommand; -import net.Chipperfluff.chipi.block.ModBlocks; -import net.Chipperfluff.chipi.item.ModItems; public class ChipiMod implements ModInitializer { @@ -26,16 +29,22 @@ public class ChipiMod implements ModInitializer { @Override public void onInitialize() { - ServerTickEvents.END_WORLD_TICK.register((ServerWorld world) -> { + ModBlocks.register(); + ModItems.register(); + + CommandRegistrationCallback.EVENT.register( + (dispatcher, registryAccess, environment) -> + ChpCommand.register(dispatcher) + ); + + ServerWorldEvents.LOAD.register((server, world) -> { if (!world.getRegistryKey().getValue().equals(CHIPI_DIM)) return; - // 🌙 Lock time world.setTimeOfDay(18000); world.getGameRules() .get(GameRules.DO_DAYLIGHT_CYCLE) - .set(false, world.getServer()); + .set(false, server); - // 🧠 persistent spawn state SpawnPlacedState state = world.getPersistentStateManager() .getOrCreate( SpawnPlacedState::fromNbt, @@ -45,41 +54,35 @@ public class ChipiMod implements ModInitializer { if (state.placed) return; - var optional = world.getStructureTemplateManager() - .getTemplate(SPAWN_STRUCTURE); + StructureTemplate spawnTemplate = world + .getStructureTemplateManager() + .getTemplate(SPAWN_STRUCTURE) + .orElse(null); - if (optional.isEmpty()) { + if (spawnTemplate == null) { System.err.println("[CHIPI] spawn.nbt not found!"); return; } - StructureTemplate template = optional.get(); + BlockPos spawnCenter = new BlockPos(0, 80, 0); - BlockPos origin = new BlockPos(0, 80, 0); - - template.place( + spawnTemplate.place( world, - origin, - origin, + spawnCenter, + spawnCenter, new StructurePlacementData(), world.getRandom(), 2 ); - world.setSpawnPos(origin.up(), 0.0f); + world.setSpawnPos(spawnCenter.up(), 0.0f); + + ChipiDungeonGenerator.generateInitialLayout(world, spawnCenter); state.placed = true; state.markDirty(); - System.out.println("[CHIPI] Spawn structure placed"); + System.out.println("[CHIPI] Spawn + initial dungeon generated"); }); - - ModBlocks.register(); - ModItems.register(); - - CommandRegistrationCallback.EVENT.register( - (dispatcher, registryAccess, environment) -> - ChipperCommand.register(dispatcher) - ); } } diff --git a/src/main/java/net/Chipperfluff/chipi/block/ChipperFrameBlock.java b/src/main/java/net/Chipperfluff/chipi/block/ChipperFrameBlock.java new file mode 100644 index 0000000..97e8772 --- /dev/null +++ b/src/main/java/net/Chipperfluff/chipi/block/ChipperFrameBlock.java @@ -0,0 +1,54 @@ +package net.Chipperfluff.chipi.block; + +import net.minecraft.block.BlockState; +import net.minecraft.block.PillarBlock; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemPlacementContext; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class ChipperFrameBlock extends PillarBlock { + + public ChipperFrameBlock(Settings settings) { + super(settings); + } + + @Override + public ActionResult onUse( + BlockState state, + World world, + BlockPos pos, + PlayerEntity player, + Hand hand, + BlockHitResult hit + ) { + return ActionResult.PASS; + } + + @Override + public void onPlaced(World world, BlockPos pos, BlockState state, + LivingEntity placer, ItemStack stack) { + if (!world.isClient) { + ChipperPortalShape.tryCreate(world, pos); + } + } + + @Override + public void onStateReplaced( + BlockState state, + World world, + BlockPos pos, + BlockState newState, + boolean moved + ) { + if (!world.isClient) { + ChipperPortalShape.destroyNearby(world, pos); + } + super.onBroken(world, pos, state); + } +} diff --git a/src/main/java/net/Chipperfluff/chipi/block/ChipperPortalBlock.java b/src/main/java/net/Chipperfluff/chipi/block/ChipperPortalBlock.java new file mode 100644 index 0000000..46af6d3 --- /dev/null +++ b/src/main/java/net/Chipperfluff/chipi/block/ChipperPortalBlock.java @@ -0,0 +1,83 @@ +package net.Chipperfluff.chipi.block; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.ShapeContext; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.util.shape.VoxelShapes; +import net.minecraft.world.BlockView; +import net.minecraft.world.World; +import net.minecraft.world.TeleportTarget; +import net.minecraft.network.packet.s2c.play.PositionFlag; +import java.util.EnumSet; + + +public class ChipperPortalBlock extends Block { + + public ChipperPortalBlock(Settings settings) { + super(settings); + } + + @Override + public VoxelShape getCollisionShape( + BlockState state, + BlockView world, + BlockPos pos, + ShapeContext context + ) { + return VoxelShapes.empty(); + } + + @Override + public void onEntityCollision( + BlockState state, + World world, + BlockPos pos, + Entity entity + ) { + if (world.isClient) return; + if (!(entity instanceof PlayerEntity player)) return; + + ServerWorld targetWorld = world.getServer().getWorld( + RegistryKey.of( + RegistryKeys.WORLD, + new Identifier("chipi", "chipi_dimension") + ) + ); + + if (targetWorld == null) return; + + TeleportTarget target = new TeleportTarget( + new Vec3d(5.5, 90, 5.5), + Vec3d.ZERO, + player.getYaw(), + player.getPitch() + ); + + player.teleport( + targetWorld, + 5.5, + 88.0, + 6.5, + EnumSet.noneOf(PositionFlag.class), + player.getYaw(), + player.getPitch() + ); + } + + @Override + public float calcBlockBreakingDelta(BlockState state, PlayerEntity player, BlockView world, BlockPos pos) { + if (!player.isCreative()) { + return 0.0F; // impossible to break + } + return super.calcBlockBreakingDelta(state, player, world, pos); + } +} diff --git a/src/main/java/net/Chipperfluff/chipi/block/ChipperPortalShape.java b/src/main/java/net/Chipperfluff/chipi/block/ChipperPortalShape.java new file mode 100644 index 0000000..c587801 --- /dev/null +++ b/src/main/java/net/Chipperfluff/chipi/block/ChipperPortalShape.java @@ -0,0 +1,103 @@ +package net.Chipperfluff.chipi.block; + +import net.minecraft.block.BlockState; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.World; + +public class ChipperPortalShape { + + private final World world; + private final BlockPos origin; + private final Direction.Axis axis; + + public ChipperPortalShape(World world, BlockPos origin, Direction.Axis axis) { + this.world = world; + this.origin = origin; + this.axis = axis; + } + + public static boolean tryCreate(World world, BlockPos placedPos) { + if (!(world instanceof ServerWorld)) return false; + + for (Direction.Axis axis : new Direction.Axis[]{Direction.Axis.X, Direction.Axis.Z}) { + ChipperPortalShape shape = new ChipperPortalShape(world, placedPos, axis); + if (shape.isValid()) { + shape.placePortal(); + System.out.println("[CHIPI] portal created at " + placedPos + " axis=" + axis); + return true; + } + } + return false; + } + + private boolean isValid() { + // find bottom-left corner + BlockPos base = findBottomLeft(); + if (base == null) return false; + + // check frame + for (int x = 0; x < 4; x++) { + for (int y = 0; y < 5; y++) { + + boolean edge = x == 0 || x == 3 || y == 0 || y == 4; + BlockPos pos = offset(base, x, y); + BlockState state = world.getBlockState(pos); + + if (edge) { + if (!state.isOf(ModBlocks.CHIPPER_FRAME)) return false; + } else { + if (!state.isAir()) return false; + } + } + } + return true; + } + + private BlockPos findBottomLeft() { + for (int dx = -3; dx <= 0; dx++) { + for (int dy = -4; dy <= 0; dy++) { + BlockPos pos = offset(origin.add(dx, dy, 0), 0, 0); + if (world.getBlockState(pos).isOf(ModBlocks.CHIPPER_FRAME)) { + return pos; + } + } + } + return null; + } + + private BlockPos offset(BlockPos pos, int x, int y) { + return axis == Direction.Axis.X + ? pos.add(0, y, x) + : pos.add(x, y, 0); + } + + private void placePortal() { + BlockPos base = findBottomLeft(); + if (base == null) return; + + for (int x = 1; x <= 2; x++) { + for (int y = 1; y <= 3; y++) { + world.setBlockState( + offset(base, x, y), + ModBlocks.CHIPPER_PORTAL.getDefaultState(), + 3 + ); + } + } + } + + public static void destroyNearby(World world, BlockPos pos) { + for (int x = -3; x <= 3; x++) { + for (int y = -4; y <= 4; y++) { + for (int z = -3; z <= 3; z++) { + BlockPos p = pos.add(x, y, z); + if (world.getBlockState(p).isOf(ModBlocks.CHIPPER_PORTAL)) { + world.setBlockState(p, world.getFluidState(p).getBlockState(), 3); + } + } + } + } + } +} diff --git a/src/main/java/net/Chipperfluff/chipi/block/ModBlocks.java b/src/main/java/net/Chipperfluff/chipi/block/ModBlocks.java index 16c88ee..d3d86dc 100644 --- a/src/main/java/net/Chipperfluff/chipi/block/ModBlocks.java +++ b/src/main/java/net/Chipperfluff/chipi/block/ModBlocks.java @@ -6,14 +6,44 @@ import net.minecraft.registry.Registries; import net.minecraft.registry.Registry; import net.minecraft.util.Identifier; import net.Chipperfluff.chipi.ChipiMod; +import net.Chipperfluff.chipi.block.ChipperFrameBlock; +import net.Chipperfluff.chipi.block.ChipperPortalBlock; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.block.MapColor; public class ModBlocks { public static final Block VOID_BLOCK = register( - "void_block", - new Block(AbstractBlock.Settings.create() - .strength(1.5f, 6.0f) - ) + "void_block", + new VoidBlock( + AbstractBlock.Settings.create() + .strength(-1.0F, 3600000.0F) // vanilla unbreakable values + .mapColor(MapColor.BLACK) + .noCollision() + .dropsNothing() + ) + ); + + public static final Block CHIPPER_FRAME = + register( + "chipper_frame", + new ChipperFrameBlock( + FabricBlockSettings + .create() + .strength(3.0f) + .requiresTool() + ) + ); + + public static final Block CHIPPER_PORTAL = register( + "chipper_portal", + new ChipperPortalBlock( + FabricBlockSettings + .create() + .noCollision() + .luminance(3) + .strength(-1.0f) + ) ); private static Block register(String name, Block block) { diff --git a/src/main/java/net/Chipperfluff/chipi/block/VoidBlock.java b/src/main/java/net/Chipperfluff/chipi/block/VoidBlock.java new file mode 100644 index 0000000..b22d3c6 --- /dev/null +++ b/src/main/java/net/Chipperfluff/chipi/block/VoidBlock.java @@ -0,0 +1,28 @@ +package net.Chipperfluff.chipi.block; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockView; +import net.minecraft.world.explosion.Explosion; + +public class VoidBlock extends Block { + + public VoidBlock(Settings settings) { + super(settings); + } + + @Override + public float calcBlockBreakingDelta( + BlockState state, + PlayerEntity player, + BlockView world, + BlockPos pos + ) { + if (!player.isCreative()) { + return 0.0F; + } + return super.calcBlockBreakingDelta(state, player, world, pos); + } +} diff --git a/src/main/java/net/Chipperfluff/chipi/client/ChipiClient.java b/src/main/java/net/Chipperfluff/chipi/client/ChipiClient.java index 10c4b7d..a2923cb 100644 --- a/src/main/java/net/Chipperfluff/chipi/client/ChipiClient.java +++ b/src/main/java/net/Chipperfluff/chipi/client/ChipiClient.java @@ -3,14 +3,18 @@ package net.Chipperfluff.chipi.client; import net.fabricmc.api.ClientModInitializer; import net.minecraft.client.render.DimensionEffects; import net.minecraft.util.Identifier; +import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap; +import net.minecraft.client.render.RenderLayer; +import net.Chipperfluff.chipi.block.ModBlocks; + public class ChipiClient implements ClientModInitializer { @Override public void onInitializeClient() { - //DimensionEffects.register( - // new Identifier("chipi", "void"), - // new VoidDimensionEffects() - //); + BlockRenderLayerMap.INSTANCE.putBlock( + ModBlocks.CHIPPER_PORTAL, + RenderLayer.getTranslucent() + ); } } diff --git a/src/main/java/net/Chipperfluff/chipi/command/ChipperCommand.java b/src/main/java/net/Chipperfluff/chipi/command/ChipperCommand.java deleted file mode 100644 index 422d0fa..0000000 --- a/src/main/java/net/Chipperfluff/chipi/command/ChipperCommand.java +++ /dev/null @@ -1,23 +0,0 @@ -package net.Chipperfluff.chipi.command; - -import com.mojang.brigadier.CommandDispatcher; -import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.text.Text; - -import static net.minecraft.server.command.CommandManager.literal; - -public class ChipperCommand { - - public static void register(CommandDispatcher dispatcher) { - dispatcher.register( - literal("chipper") - .executes(context -> { - context.getSource().sendFeedback( - () -> Text.literal("This is a test command!"), - false - ); - return 1; - }) - ); - } -} diff --git a/src/main/java/net/Chipperfluff/chipi/command/ChpCommand.java b/src/main/java/net/Chipperfluff/chipi/command/ChpCommand.java new file mode 100644 index 0000000..d3b7ce5 --- /dev/null +++ b/src/main/java/net/Chipperfluff/chipi/command/ChpCommand.java @@ -0,0 +1,49 @@ +package net.Chipperfluff.chipi.command; + +import com.mojang.brigadier.CommandDispatcher; +import net.Chipperfluff.chipi.world.gen.struct.ChipiStructures; +import net.minecraft.command.argument.BlockPosArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.text.Text; +import net.minecraft.util.math.BlockPos; + +public class ChpCommand { + + public static void register(CommandDispatcher dispatcher) { + + dispatcher.register( + CommandManager.literal("chp") + .then(CommandManager.argument("name", StringArgumentType.word()) + .then(CommandManager.argument("pos", BlockPosArgumentType.blockPos()) + .executes(ctx -> { + + String name = + StringArgumentType.getString(ctx, "name"); + + BlockPos center = + BlockPosArgumentType.getBlockPos(ctx, "pos"); + + ServerWorld world = + ctx.getSource().getWorld(); + + var structure = + ChipiStructures.get(name); + + if (structure == null) { + ctx.getSource().sendError( + Text.literal("Unknown structure: " + name) + ); + return 0; + } + + structure.place(world, center); + return 1; + }) + ) + ) + ); + } +} diff --git a/src/main/java/net/Chipperfluff/chipi/item/ModItems.java b/src/main/java/net/Chipperfluff/chipi/item/ModItems.java index 864047f..535becd 100644 --- a/src/main/java/net/Chipperfluff/chipi/item/ModItems.java +++ b/src/main/java/net/Chipperfluff/chipi/item/ModItems.java @@ -15,6 +15,16 @@ public class ModItems { new BlockItem(ModBlocks.VOID_BLOCK, new Item.Settings()) ); + public static final Item CHIPPER_FRAME = register( + "chipper_frame", + new BlockItem(ModBlocks.CHIPPER_FRAME, new Item.Settings()) + ); + + public static final Item CHIPPER_PORTAL = register( + "chipper_portal", + new BlockItem(ModBlocks.CHIPPER_PORTAL, new Item.Settings()) + ); + private static Item register(String name, Item item) { return Registry.register( Registries.ITEM, diff --git a/src/main/java/net/Chipperfluff/chipi/world/gen/ChipiDungeonGenerator.java b/src/main/java/net/Chipperfluff/chipi/world/gen/ChipiDungeonGenerator.java new file mode 100644 index 0000000..813ffe7 --- /dev/null +++ b/src/main/java/net/Chipperfluff/chipi/world/gen/ChipiDungeonGenerator.java @@ -0,0 +1,45 @@ +package net.Chipperfluff.chipi.world.gen; + +import net.Chipperfluff.chipi.world.gen.struct.CorridorNSStructure; +import net.Chipperfluff.chipi.world.gen.struct.RoomBaseStructure; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; + +public class ChipiDungeonGenerator { + + private ChipiDungeonGenerator() {} + + public static void generateInitialLayout(ServerWorld world, BlockPos portalSpawnPos) { + + // Y is constant – dungeon is 2D + BlockPos center = new BlockPos( + portalSpawnPos.getX(), + portalSpawnPos.getY(), + portalSpawnPos.getZ() + ); + + // 1️⃣ FIRST ROOM (THIS IS THE CENTER) + RoomBaseStructure room = new RoomBaseStructure(); + room.place(world, center); + + // 2️⃣ SOUTH CORRIDOR (NO CENTER, JUST OFFSET) + CorridorNSStructure corridor = new CorridorNSStructure(); + + BlockPos corridorAnchor = center.add( + 0, + 0, + 9 // room south edge + 1 + ); + + corridor.place(world, corridorAnchor); + + // 3️⃣ SECOND ROOM AT END OF CORRIDOR + BlockPos secondRoomCenter = corridorAnchor.add( + 0, + 0, + 10 // full corridor length + ); + + room.place(world, secondRoomCenter); + } +} diff --git a/src/main/java/net/Chipperfluff/chipi/world/gen/ChipiStructure.java b/src/main/java/net/Chipperfluff/chipi/world/gen/ChipiStructure.java new file mode 100644 index 0000000..fa8d0d9 --- /dev/null +++ b/src/main/java/net/Chipperfluff/chipi/world/gen/ChipiStructure.java @@ -0,0 +1,43 @@ +package net.Chipperfluff.chipi.world.gen; + +import net.minecraft.server.world.ServerWorld; +import net.minecraft.structure.StructurePlacementData; +import net.minecraft.structure.StructureTemplate; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; + +public abstract class ChipiStructure { + + protected final Identifier id; + + protected ChipiStructure(Identifier id) { + this.id = id; + } + + /** Override this per structure */ + protected abstract BlockPos centerToOrigin(BlockPos center, StructureTemplate template); + + public void place(ServerWorld world, BlockPos center) { + var opt = world.getStructureTemplateManager().getTemplate(id); + + if (opt.isEmpty()) { + System.out.println("[CHIPI] Missing structure: " + id); + return; + } + + StructureTemplate template = opt.get(); + BlockPos origin = centerToOrigin(center, template); + + template.place( + world, + origin, + origin, + new StructurePlacementData(), + world.getRandom(), + 2 + ); + + System.out.println("[CHIPI] Placed " + id + " at center " + center + + " (origin " + origin + ")"); + } +} diff --git a/src/main/java/net/Chipperfluff/chipi/world/gen/struct/ChipiStructures.java b/src/main/java/net/Chipperfluff/chipi/world/gen/struct/ChipiStructures.java new file mode 100644 index 0000000..c34ccc0 --- /dev/null +++ b/src/main/java/net/Chipperfluff/chipi/world/gen/struct/ChipiStructures.java @@ -0,0 +1,21 @@ +package net.Chipperfluff.chipi.world.gen.struct; + +import net.Chipperfluff.chipi.world.gen.ChipiStructure; // <-- THIS WAS MISSING +import java.util.HashMap; +import java.util.Map; + +public class ChipiStructures { + + private static final Map REGISTRY = new HashMap<>(); + + static { + REGISTRY.put("spawn", new SpawnStructure()); + REGISTRY.put("room_base", new RoomBaseStructure()); + REGISTRY.put("corridor_ns", new CorridorNSStructure()); + REGISTRY.put("corridor_ew", new CorridorEWStructure()); + } + + public static ChipiStructure get(String name) { + return REGISTRY.get(name); + } +} diff --git a/src/main/java/net/Chipperfluff/chipi/world/gen/struct/CorridorEWStructure.java b/src/main/java/net/Chipperfluff/chipi/world/gen/struct/CorridorEWStructure.java new file mode 100644 index 0000000..94e8e06 --- /dev/null +++ b/src/main/java/net/Chipperfluff/chipi/world/gen/struct/CorridorEWStructure.java @@ -0,0 +1,25 @@ +package net.Chipperfluff.chipi.world.gen.struct; + +import net.Chipperfluff.chipi.world.gen.ChipiStructure; +import net.minecraft.structure.StructureTemplate; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3i; + +public class CorridorEWStructure extends ChipiStructure { + + public CorridorEWStructure() { + super(new Identifier("chipi", "coridor_straight_ew")); + } + + @Override + protected BlockPos centerToOrigin(BlockPos center, StructureTemplate template) { + Vec3i size = template.getSize(); + // west side centered at (0,0,0) + return center.add( + 0, + 0, + 0 + ); + } +} diff --git a/src/main/java/net/Chipperfluff/chipi/world/gen/struct/CorridorNSStructure.java b/src/main/java/net/Chipperfluff/chipi/world/gen/struct/CorridorNSStructure.java new file mode 100644 index 0000000..28cb1a9 --- /dev/null +++ b/src/main/java/net/Chipperfluff/chipi/world/gen/struct/CorridorNSStructure.java @@ -0,0 +1,25 @@ +package net.Chipperfluff.chipi.world.gen.struct; + +import net.Chipperfluff.chipi.world.gen.ChipiStructure; +import net.minecraft.structure.StructureTemplate; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3i; + +public class CorridorNSStructure extends ChipiStructure { + + public CorridorNSStructure() { + super(new Identifier("chipi", "coridor_straight_ns")); + } + + @Override + protected BlockPos centerToOrigin(BlockPos center, StructureTemplate template) { + Vec3i size = template.getSize(); + //north side centered at (0,0,0) + return center.add( + -2, + -2, + 0 + ); + } +} diff --git a/src/main/java/net/Chipperfluff/chipi/world/gen/struct/RoomBaseStructure.java b/src/main/java/net/Chipperfluff/chipi/world/gen/struct/RoomBaseStructure.java new file mode 100644 index 0000000..e7aeca4 --- /dev/null +++ b/src/main/java/net/Chipperfluff/chipi/world/gen/struct/RoomBaseStructure.java @@ -0,0 +1,25 @@ +package net.Chipperfluff.chipi.world.gen.struct; + +import net.Chipperfluff.chipi.world.gen.ChipiStructure; +import net.minecraft.structure.StructureTemplate; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3i; + +public class RoomBaseStructure extends ChipiStructure { + + public RoomBaseStructure() { + super(new Identifier("chipi", "room_base")); + } + + @Override + protected BlockPos centerToOrigin(BlockPos center, StructureTemplate template) { + Vec3i size = template.getSize(); + + return center.add( + -9, + -1, + -8 + ); + } +} diff --git a/src/main/java/net/Chipperfluff/chipi/world/gen/struct/SpawnStructure.java b/src/main/java/net/Chipperfluff/chipi/world/gen/struct/SpawnStructure.java new file mode 100644 index 0000000..3b1ec5d --- /dev/null +++ b/src/main/java/net/Chipperfluff/chipi/world/gen/struct/SpawnStructure.java @@ -0,0 +1,25 @@ +package net.Chipperfluff.chipi.world.gen.struct; + +import net.Chipperfluff.chipi.world.gen.ChipiStructure; +import net.minecraft.structure.StructureTemplate; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3i; + +public class SpawnStructure extends ChipiStructure { + + public SpawnStructure() { + super(new Identifier("chipi", "spawn")); + } + + @Override + protected BlockPos centerToOrigin(BlockPos center, StructureTemplate template) { + Vec3i size = template.getSize(); + + return center.add( + -5, + -9, + -6 + ); + } +} diff --git a/src/main/resources/assets/chipi/blockstates/chipper_frame.json b/src/main/resources/assets/chipi/blockstates/chipper_frame.json new file mode 100644 index 0000000..a2f6307 --- /dev/null +++ b/src/main/resources/assets/chipi/blockstates/chipper_frame.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "chipi:block/chipper_frame" } + } +} diff --git a/src/main/resources/assets/chipi/blockstates/chipper_portal.json b/src/main/resources/assets/chipi/blockstates/chipper_portal.json new file mode 100644 index 0000000..b33dbc1 --- /dev/null +++ b/src/main/resources/assets/chipi/blockstates/chipper_portal.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "chipi:block/chipper_portal" + } + } +} diff --git a/src/main/resources/assets/chipi/models/block/chipper_frame.json b/src/main/resources/assets/chipi/models/block/chipper_frame.json new file mode 100644 index 0000000..512ca97 --- /dev/null +++ b/src/main/resources/assets/chipi/models/block/chipper_frame.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "chipi:block/chipper_frame" + } +} diff --git a/src/main/resources/assets/chipi/models/block/chipper_portal.json b/src/main/resources/assets/chipi/models/block/chipper_portal.json new file mode 100644 index 0000000..a864400 --- /dev/null +++ b/src/main/resources/assets/chipi/models/block/chipper_portal.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "chipi:block/chipper_portal" + } +} diff --git a/src/main/resources/assets/chipi/models/item/chipper_frame.json b/src/main/resources/assets/chipi/models/item/chipper_frame.json new file mode 100644 index 0000000..a4425f5 --- /dev/null +++ b/src/main/resources/assets/chipi/models/item/chipper_frame.json @@ -0,0 +1,3 @@ +{ + "parent": "chipi:block/chipper_frame" +} diff --git a/src/main/resources/assets/chipi/models/item/chipper_portal.json b/src/main/resources/assets/chipi/models/item/chipper_portal.json new file mode 100644 index 0000000..4366efe --- /dev/null +++ b/src/main/resources/assets/chipi/models/item/chipper_portal.json @@ -0,0 +1,3 @@ +{ + "parent": "chipi:block/chipper_portal" +} diff --git a/src/main/resources/assets/chipi/textures/block/chipper_frame.png b/src/main/resources/assets/chipi/textures/block/chipper_frame.png new file mode 100644 index 0000000000000000000000000000000000000000..538f4386f1c6c354b7926c3e3a61660217a46601 GIT binary patch literal 1294 zcmV+p1@ZccP)EX>4Tx04R}tkv&MmKpe$iQ>7x64lN?$kfA!+!4Jf-R-p(LO0CeUgUO{|(4-+r zad8w}3l4rPRvlcNb#-tR1i=pwcfm=~MN0f%QfLw5!Ery{-Fw`<1B7Oksb)_cP&LcQ zBoksTzbXb^5kv@sm_S%!raqTWr{Foh?&0I>U5saWpZjwRCe3JS*_Mt`=0!T!GgB3%ypV0NMaF7NJE5-Ix48bLX1|86cZWRPk8u;9eX zeSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00QPoL_t(I%O%pwZd+9tK;iZ8ea`XmC64XbZbH-4YSchb zAmSp(ph$=b14c+4G2(4_luj5jU{Dx3duPU7U)j_-T#pFv(=4L&Wo z_hqVknWvH(7K(XjLPIF3^mX zq7|x<@JPed*X?%2`hPJqhvTF;o~=yS`1we zu~GI$!jI2AU%g`(Mwa%a@W($Bjs(n|R?OF{!p$Ol`)JCnKcSJr*afOKFq|m=bv)}$ z;k#|6KUU_AXR%_amjr{tqYms3guCw>im~$ZsZdG`xd?Kx@LpZeRQRxEC`5`k&=KKA z*rJ5>YJ-Ahk z7-A^qEiY#=$y~zidPD@UP%xBhhP86Qig2SK?2QGdidT5|nk6YIV+X}CjIHqSV0sle zz4R}G+EGc2V_KeDvugvS@kFUO9zhp=lMrJ7|wbI3QJ#=5nVa5+^v z15bCXJU#JjHH62Ra(&KlbQ#D-aJz00pg&dSk}I$O0WRKRe2(LhZvX%Q07*qoM6N<$ Ef{e^g%K!iX literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/chipi/textures/block/chipper_portal.png b/src/main/resources/assets/chipi/textures/block/chipper_portal.png new file mode 100644 index 0000000000000000000000000000000000000000..7b6e9108cd0b5ee5da616fa9098bc36c092071fc GIT binary patch literal 1146 zcmV-=1cm#FP)EX>4Tx04R}tkv&MmKpe$iQ>7x64lN?$kfA!+!4Jf-R-p(LO0CeUgUO{|(4-+r zad8w}3l4rPRvlcNb#-tR1i=pwcfm=~MN0f%QfLw5!Ery{-Fw`<1B7Oksb)_cP&LcQ zBoksTzbXb^5kv@sm_S%!raqTWr{Foh?&0I>U5saWpZjwRCe3JS*_Mt`=0!T!GgB3%ypV0NMaF7NJE5-Ix48bLX1|86cZWRPk8u;9eX zeSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00L4;L_t(I%Vmr|153SFyMT32~&7sup!hefkrB`N9F19bn@H zd3ZzEZP0iE6j<{Rn=b)DhZ2paASE3B4vQlSHh%#?`xLsjNbT5cAM6J-FOl*XQ9vuC zc>v1*@f=^j!tyT^e)tSoy@u5*)VE*{$@4=Hh4F8MYp@ot=eQgo@e`KM@Y!7$KBCsW z#r-|_77)JC>{p-y-+>Qs^(Uz~CC?mJMZ0qf3kh$J3ELVOB9c1944dDA-32gzNvX#U0Y#5jymQ_yHY8tgf(G3!V`h0no8S<2hm#a0Mu;{Rcwq z(0+^9h?RRt+8}wDDs90H0I8lJ)z4rb!42(BPyp%APL#cF%mn;NpCcL%Z;#Gr-V$A`>HA?ji_WziW{Z7REX>4Tx04R}tkv&MmKpe$iQ>7x64lN?$kfA!+!4Jf-R-p(LO0CeUgUO{|(4-+r zad8w}3l4rPRvlcNb#-tR1i=pwcfm=~MN0f%QfLw5!Ery{-Fw`<1B7Oksb)_cP&LcQ zBoksTzbXb^5kv@sm_S%!raqTWr{Foh?&0I>U5saWpZjwRCe3JS*_Mt`=0!T!GgB3%ypV0NMaF7NJE5-Ix48bLX1|86cZWRPk8u;9eX zeSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00QPoL_t(I%O%pwZd+9tK;iZ8ea`XmC64XbZbH-4YSchb zAmSp(ph$=b14c+4G2(4_luj5jU{Dx3duPU7U)j_-T#pFv(=4L&Wo z_hqVknWvH(7K(XjLPIF3^mX zq7|x<@JPed*X?%2`hPJqhvTF;o~=yS`1we zu~GI$!jI2AU%g`(Mwa%a@W($Bjs(n|R?OF{!p$Ol`)JCnKcSJr*afOKFq|m=bv)}$ z;k#|6KUU_AXR%_amjr{tqYms3guCw>im~$ZsZdG`xd?Kx@LpZeRQRxEC`5`k&=KKA z*rJ5>YJ-Ahk z7-A^qEiY#=$y~zidPD@UP%xBhhP86Qig2SK?2QGdidT5|nk6YIV+X}CjIHqSV0sle zz4R}G+EGc2V_KeDvugvS@kFUO9zhp=lMrJ7|wbI3QJ#=5nVa5+^v z15bCXJU#JjHH62Ra(&KlbQ#D-aJz00pg&dSk}I$O0WRKRe2(LhZvX%Q07*qoM6N<$ Ef{e^g%K!iX literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/chipi/textures/item/chipper_portal.png b/src/main/resources/assets/chipi/textures/item/chipper_portal.png new file mode 100644 index 0000000000000000000000000000000000000000..7b6e9108cd0b5ee5da616fa9098bc36c092071fc GIT binary patch literal 1146 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