broken room generator

This commit is contained in:
Chipperfluff 2025-12-16 05:43:30 +01:00
parent 90ca08146a
commit bbd5a78a7f
30 changed files with 640 additions and 60 deletions

View File

@ -1,17 +1,20 @@
package net.Chipperfluff.chipi; 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.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; 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.server.world.ServerWorld;
import net.minecraft.structure.StructurePlacementData;
import net.minecraft.structure.StructureTemplate;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.GameRules; 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 { public class ChipiMod implements ModInitializer {
@ -26,16 +29,22 @@ public class ChipiMod implements ModInitializer {
@Override @Override
public void onInitialize() { 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; if (!world.getRegistryKey().getValue().equals(CHIPI_DIM)) return;
// 🌙 Lock time
world.setTimeOfDay(18000); world.setTimeOfDay(18000);
world.getGameRules() world.getGameRules()
.get(GameRules.DO_DAYLIGHT_CYCLE) .get(GameRules.DO_DAYLIGHT_CYCLE)
.set(false, world.getServer()); .set(false, server);
// 🧠 persistent spawn state
SpawnPlacedState state = world.getPersistentStateManager() SpawnPlacedState state = world.getPersistentStateManager()
.getOrCreate( .getOrCreate(
SpawnPlacedState::fromNbt, SpawnPlacedState::fromNbt,
@ -45,41 +54,35 @@ public class ChipiMod implements ModInitializer {
if (state.placed) return; if (state.placed) return;
var optional = world.getStructureTemplateManager() StructureTemplate spawnTemplate = world
.getTemplate(SPAWN_STRUCTURE); .getStructureTemplateManager()
.getTemplate(SPAWN_STRUCTURE)
.orElse(null);
if (optional.isEmpty()) { if (spawnTemplate == null) {
System.err.println("[CHIPI] spawn.nbt not found!"); System.err.println("[CHIPI] spawn.nbt not found!");
return; return;
} }
StructureTemplate template = optional.get(); BlockPos spawnCenter = new BlockPos(0, 80, 0);
BlockPos origin = new BlockPos(0, 80, 0); spawnTemplate.place(
template.place(
world, world,
origin, spawnCenter,
origin, spawnCenter,
new StructurePlacementData(), new StructurePlacementData(),
world.getRandom(), world.getRandom(),
2 2
); );
world.setSpawnPos(origin.up(), 0.0f); world.setSpawnPos(spawnCenter.up(), 0.0f);
ChipiDungeonGenerator.generateInitialLayout(world, spawnCenter);
state.placed = true; state.placed = true;
state.markDirty(); 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)
);
} }
} }

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}
}
}
}

View File

@ -6,13 +6,43 @@ import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry; import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.Chipperfluff.chipi.ChipiMod; 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 class ModBlocks {
public static final Block VOID_BLOCK = register( public static final Block VOID_BLOCK = register(
"void_block", "void_block",
new Block(AbstractBlock.Settings.create() new VoidBlock(
.strength(1.5f, 6.0f) 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)
) )
); );

View File

@ -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);
}
}

View File

@ -3,14 +3,18 @@ package net.Chipperfluff.chipi.client;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import net.minecraft.client.render.DimensionEffects; import net.minecraft.client.render.DimensionEffects;
import net.minecraft.util.Identifier; 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 { public class ChipiClient implements ClientModInitializer {
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
//DimensionEffects.register( BlockRenderLayerMap.INSTANCE.putBlock(
// new Identifier("chipi", "void"), ModBlocks.CHIPPER_PORTAL,
// new VoidDimensionEffects() RenderLayer.getTranslucent()
//); );
} }
} }

View File

@ -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<ServerCommandSource> dispatcher) {
dispatcher.register(
literal("chipper")
.executes(context -> {
context.getSource().sendFeedback(
() -> Text.literal("This is a test command!"),
false
);
return 1;
})
);
}
}

View File

@ -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<ServerCommandSource> 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;
})
)
)
);
}
}

View File

@ -15,6 +15,16 @@ public class ModItems {
new BlockItem(ModBlocks.VOID_BLOCK, new Item.Settings()) 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) { private static Item register(String name, Item item) {
return Registry.register( return Registry.register(
Registries.ITEM, Registries.ITEM,

View File

@ -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);
}
}

View File

@ -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 + ")");
}
}

View File

@ -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<String, ChipiStructure> 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);
}
}

View File

@ -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
);
}
}

View File

@ -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
);
}
}

View File

@ -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
);
}
}

View File

@ -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
);
}
}

View File

@ -0,0 +1,5 @@
{
"variants": {
"": { "model": "chipi:block/chipper_frame" }
}
}

View File

@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "chipi:block/chipper_portal"
}
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:block/cube_all",
"textures": {
"all": "chipi:block/chipper_frame"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:block/cube_all",
"textures": {
"all": "chipi:block/chipper_portal"
}
}

View File

@ -0,0 +1,3 @@
{
"parent": "chipi:block/chipper_frame"
}

View File

@ -0,0 +1,3 @@
{
"parent": "chipi:block/chipper_portal"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.