Skip to content

Manipulating Jigsaws

This is where the main event begins - generating a randomized dungeon or whatever you want using all the knowledge you learned for Loquat.

Note

In my imagination, there are many many illustrations here. But I'm too lazy. So please use your imagination instead.

The preparation

Info

The vanilla data pack can be found [here](https://github.com/misode/mcmeta/tree/data).

Here I assume you already have some knowledge of how to use data pack to generate a jigsaw structure. So let's do a quick overview:

1. Create our structure type

Example

data/pack/worldgen/structure/big_dungeon.json:
```json
{
  "type": "minecraft:jigsaw",
  "biomes": "#minecraft:is_overworld",
  "max_distance_from_center": 80,
  "project_start_to_heightmap": "WORLD_SURFACE_WG",
  "size": 1,
  "spawn_overrides": {},
  "start_height": {
    "absolute": 40
  },
  "start_pool": "pack:start",
  "step": "surface_structures",
  "terrain_adaptation": "none",
  "use_expansion_hack": false
}
```

2. Build and add the rooms

Now we need to build every part of our structure, place jigsaw blocks so that they can connect with each other, and save them as .nbt files using the Structure Block. Remember to create an area with the same size as the room, so that we can clear the whole structure in world when we want to.

All the .nbt files should be placed in the data/{namespace}}/structures/ directory. In our case, it is data/pack/structures/.

3. Create template pools

Example

data/pack/worldgen/template_pool/start.json:
```json
{
  "name": "pack:start",
  "elements": [
    {
      "element": {
        "element_type": "minecraft:single_pool_element",
        "location": "pack:start",
        "processors": "minecraft:empty",
        "projection": "rigid"
      },
      "weight": 1
    }
  ],
  "fallback": "minecraft:empty"
}
```

Now you should be able to generate your structure using the /place structure command.

The placement script

Here we need to get the generator to connect these rooms in a way that we are happy with. Ideally, we want our dungeon to start with a start room, followed by five monster rooms, and a boss room at the end. We also want to have a treasure room to be placed as a branch for one of the monster rooms.

This is the KubeJS script being used:

// server script
ServerEvents.recipes(event => {
    LoquatPlacements.register("any_key_you_like", new LoquatTreeNodePlacer("pack:big_dungeon", ctx => {
        // add monster rooms
        let monsterRooms = []
        // ctx.root is the start room according to the structure json file
        let lastRoom = ctx.root
        for (let i = 0; i < 5; i++) {
            // lastRoom.addChild(joint_name, template_pool_name)
            monsterRooms.push(lastRoom = lastRoom.addChild("door", "pack:monster"))
        }
        // add boss room
        lastRoom.addChild("door", "pack:boss")
        // add treasure room
        monsterRooms[ctx.random.nextInt(monsterRooms.length)].addChild("door", "pack:treasure")
        monsterRooms.forEach(room => {
            // we don't want the entrance and exit to be too close, so we set a minimum distance
            room.minEdgeDistance = 8
            // we don't want two rooms are the same, so we make a unique group id for them
            room.uniqueGroup = "monster"
        })
    }))
})

Reload the scripts and run /place structure pack:big_dungeon, ta-da!

Placing the structure

By command

Generally, we use KubeJS to execute the /place structure command, to place the structure. Here is an example:

Example

```js
// server script
BlockEvents.rightClicked('minecraft:diamond_block', event => {
    if (event.hand != 'MAIN_HAND') {
        return
    }
    event.server.runCommandSilent(`execute in minecraft:overworld positioned 0 0 0 run place structure pack:big_dungeon`)
})
```

Note

Do NOT use `/execute run place structure <structure> <x> <y> <z>`, it is not supported.

Use `/execute positioned <x> <y> <z> run place structure <structure>` instead.

During world generation

You can define a structure set to randomly place the structure like vanilla does.

Example

data/pack/worldgen/structure_set/big_dungeon.json:
```json
{
  "placement": {
    "type": "minecraft:random_spread",
    "salt": 100,
    "separation": 8,
    "spacing": 15
  },
  "structures": [
    {
      "structure": "pack:big_dungeon",
      "weight": 1
    }
  ]
}
```

Fallback piece

So it looks like our dungeon has been bui... Wait, how does the player get out of the dungeon?

Recall that we have a "branch room", which means that the monster room may have multiple exits, which means that there may be exits that are not used. We need to place a "lid" to block the player's way out when the exit has no target room.

First you need to create a barrier structure and the template pool that can perfectly cover the exit of the room and fit into the corresponding jigsaw block.

Then modify the placement script:

// server script
ServerEvents.recipes(event => {
    LoquatPlacements.register("any_key_you_like", new LoquatTreeNodePlacer("pack:big_dungeon", ctx => {
        // omit the code above
        // ...
        // use node(the root).walk() to iterate all child nodes
        ctx.root.walk(room => {
            room.fallbackNodeProvider = joint => {
                if (joint !== "minecraft:door")
                    return null;
                // LoquatTreeNode is also the type of ctx.root and room
                let fallback = new LoquatTreeNode("pack:barrier")
                // cancel the offset so that the barrier will be pulled one block back
                fallback.offsetTowardsJigsawFront = false
                // cancel the collision check so that the barrier can be placed inside or intersect with other structure pieces
                fallback.checkForCollisions = false
                return fallback
            }
        })
    }))
})