Six-Room Dungeon Diagram Generator, based on BSD by Traverse Fantasy

Based on Bite-Sized Dungeons by Marcia B.

Clicking the button below generates a dungeon; randomizes its contents, connections, and entrance(s); and displays it as a mermaid diagram.

This could surely be improved by someone with more knowledge of javascript and mermaid.js, but I’ve been using Marcia’s method since the original blog post to create dungeons and I wanted to make a generator for it for my personal use.

In the future, I’ll probably replace the text with some fun icons, and maybe I can add an option to layer up to three dungeons together. I usually use two or three bite-sized dungeons to make a single one for my home game. Maybe I could also add some capability to download the output SVG as a PNG. (That’s done thanks to this Stackoverflow answer). Also I was only able to get to this to work in Mermaid 7. Mermaid is very finnicky to work with it turns out, at least for me.

Select to reveal Javascript
	/// stuff I took from stackoverflow to make this work, only works in mermaid 7

const appendDiagramTo = (el) => (code) => el.outerHTML = code

function renderDiagram(id, el) {
  mermaid.render(
    id,
    el.textContent,
    appendDiagramTo(el)
  )
}

// randomizing and sorting stuff

const sortAlphaNum = (a, b) => a.localeCompare(b, 'en', { numeric: true }); // for alphanumerically sorting the rooms

function getRandomInt(max) {
  return Math.floor(Math.random() * max); // random number function
};

// there is probably quite a bit more splicing and dicing of arrays that was completely necessary. I'm sure this could have been simpler to do

function dngngn() {
  //all of our rooms
  var rooms = [1,2,3,4,5,6];
  //extract and define the monster rooms separately
  var monsterRoom = rooms.splice(getRandomInt(6),1) + " Monster";
  var monsterTreasureRoom = rooms.splice(getRandomInt(5),1) + " Monster & Treasure";
  //extract the interactive room from the rest of the remaining empty rooms
  var interactiveRoom = rooms.splice(getRandomInt(4),1) + " Interactive";
  // define the empty rooms in a single array, in order of treasure room + 2 empty rooms
  var emptyRooms = [rooms.splice(getRandomInt(3),1) + " Treasure",   rooms.splice(getRandomInt(2),1) + " Empty Room", rooms.splice(getRandomInt(1),1) + " Empty"];
  // lay a trap in one of the empty rooms
  var trapRoom = [emptyRooms.splice(getRandomInt(3),1) + ", Trapped"];
  // regroup the rooms array back into all of the rooms and flatten them out
  rooms = [interactiveRoom, monsterTreasureRoom, monsterRoom, trapRoom, emptyRooms].flat();
  // find your entrances: this was weirdly more complicated than I thought it would be. Maybe there was an easier way
  var entrances = [];
  var b = getRandomInt(2) + 1; // randomize whether there are 1 or 2 entrances
  var n = 6; // define the number six. I promise, it matters.
  for (b; b > 0; b--){
    entrances.push(rooms.splice(getRandomInt(n),1) + ", Entrance");
    if (b == 2) {
      n--; 
// basically, if there are 2 entrances, the second time you go around this loop, you want to reduce the numbers you randomize through to 5 (subtract 1 from n). But if there is only 1 entrance, you still want n to be 6 from the beginning. So that's why I defined n as 6. Is there a simpler way?
    }  
  }
  var  dungeon = rooms.concat(entrances).sort(sortAlphaNum); // put all the rooms in one array, ordered 1-6

  for (var i = 0; i < dungeon.length; i++) {
    dungeon[i] = i + 1 + "((" + dungeon[i] + "))"; // add the proper formatting for the mermaid graph
  }

  var passages = ["|long hall|", "|stuck/locked door|", "|secret door|", "|short hall or door|", "|short hall or door|", "|short hall or door|"]; // here are all the connections with the proper mermaid formatting

  // what follows is a complete list of all 13 BSD configurations written in mermaid. Each room is already randomized and the passages in between are randomized 
  const dungeonLayouts = [
  dungeon[1-1] + "---" + passages[getRandomInt(6)] + dungeon[2-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[3-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[4-1] + "\n" +dungeon[3-1] + "---" + passages[getRandomInt(6)] + dungeon[4-1] + "\n" +dungeon[4-1] + "---" + passages[getRandomInt(6)] + dungeon[5-1] + "\n" +dungeon[5-1] + "---" + passages[getRandomInt(6)] + dungeon[6-1] + "\n",
  dungeon[1-1] + "---" + passages[getRandomInt(6)] + dungeon[2-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[3-1] + "\n" +dungeon[1-1] + "---" + passages[getRandomInt(6)] + dungeon[4-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[4-1] + "\n" +dungeon[4-1] + "---" + passages[getRandomInt(6)] + dungeon[5-1] + "\n" +dungeon[4-1] + "---" + passages[getRandomInt(6)] + dungeon[6-1] + "\n",
  dungeon[1-1] + "---" + passages[getRandomInt(6)] + dungeon[2-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[3-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[4-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[5-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[6-1] + "\n" +dungeon[5-1] + "---" + passages[getRandomInt(6)] + dungeon[6-1] + "\n",
  dungeon[1-1] + "---" + passages[getRandomInt(6)] + dungeon[2-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[3-1] + "\n" +dungeon[3-1] + "---" + passages[getRandomInt(6)] + dungeon[4-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[4-1] + "\n" +dungeon[4-1] + "---" + passages[getRandomInt(6)] + dungeon[5-1] + "\n" +dungeon[3-1] + "---" + passages[getRandomInt(6)] + dungeon[6-1] + "\n",
  dungeon[1-1] + "---" + passages[getRandomInt(6)] + dungeon[2-1] + "\n" +dungeon[1-1] + "---" + passages[getRandomInt(6)] + dungeon[3-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[3-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[4-1] + "\n" +dungeon[4-1] + "---" + passages[getRandomInt(6)] + dungeon[5-1] + "\n" +dungeon[5-1] + "---" + passages[getRandomInt(6)] + dungeon[6-1] + "\n",
  dungeon[1-1] + "---" + passages[getRandomInt(6)] + dungeon[2-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[3-1] + "\n" +dungeon[3-1] + "---" + passages[getRandomInt(6)] + dungeon[4-1] + "\n" +dungeon[3-1] + "---" + passages[getRandomInt(6)] + dungeon[5-1] + "\n" +dungeon[3-1] + "---" + passages[getRandomInt(6)] + dungeon[6-1] + "\n" +dungeon[5-1] + "---" + passages[getRandomInt(6)] + dungeon[6-1] + "\n",
  dungeon[1-1] + "---" + passages[getRandomInt(6)] + dungeon[2-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[4-1] + "\n" +dungeon[4-1] + "---" + passages[getRandomInt(6)] + dungeon[3-1] + "\n" +dungeon[3-1] + "---" + passages[getRandomInt(6)] + dungeon[6-1] + "\n" +dungeon[4-1] + "---" + passages[getRandomInt(6)] + dungeon[5-1] + "\n" +dungeon[5-1] + "---" + passages[getRandomInt(6)] + dungeon[6-1] + "\n",
  dungeon[1-1] + "---" + passages[getRandomInt(6)] + dungeon[2-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[4-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[3-1] + "\n" +dungeon[4-1] + "---" + passages[getRandomInt(6)] + dungeon[5-1] + "\n" +dungeon[5-1] + "---" + passages[getRandomInt(6)] + dungeon[6-1] + "\n" +dungeon[3-1] + "---" + passages[getRandomInt(6)] + dungeon[5-1] + "\n",
  dungeon[1-1] + "---" + passages[getRandomInt(6)] + dungeon[2-1] + "\n" +dungeon[1-1] + "---" + passages[getRandomInt(6)] + dungeon[3-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[4-1] + "\n" +dungeon[3-1] + "---" + passages[getRandomInt(6)] + dungeon[4-1] + "\n" +dungeon[3-1] + "---" + passages[getRandomInt(6)] + dungeon[5-1] + "\n" +dungeon[3-1] + "---" + passages[getRandomInt(6)] + dungeon[6-1] + "\n",
  dungeon[1-1] + "---" + passages[getRandomInt(6)] + dungeon[2-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[3-1] + "\n" +dungeon[3-1] + "---" + passages[getRandomInt(6)] + dungeon[4-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[5-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[6-1] + "\n" +dungeon[4-1] + "---" + passages[getRandomInt(6)] + dungeon[5-1] + "\n",
  dungeon[1-1] + "---" + passages[getRandomInt(6)] + dungeon[2-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[4-1] + "\n" +dungeon[1-1] + "---" + passages[getRandomInt(6)] + dungeon[3-1] + "\n" +dungeon[3-1] + "---" + passages[getRandomInt(6)] + dungeon[4-1] + "\n" +dungeon[3-1] + "---" + passages[getRandomInt(6)] + dungeon[5-1] + "\n" +dungeon[5-1] + "---" + passages[getRandomInt(6)] + dungeon[6-1] + "\n",
  dungeon[1-1] + "---" + passages[getRandomInt(6)] + dungeon[2-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[3-1] + "\n" +dungeon[3-1] + "---" + passages[getRandomInt(6)] + dungeon[4-1] + "\n" +dungeon[4-1] + "---" + passages[getRandomInt(6)] + dungeon[5-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[5-1] + "\n" +dungeon[5-1] + "---" + passages[getRandomInt(6)] + dungeon[6-1] + "\n",
  dungeon[1-1] + "---" + passages[getRandomInt(6)] + dungeon[2-1] + "\n" +dungeon[2-1] + "---" + passages[getRandomInt(6)] + dungeon[3-1] + "\n" +dungeon[3-1] + "---" + passages[getRandomInt(6)] + dungeon[4-1] + "\n" +dungeon[4-1] + "---" + passages[getRandomInt(6)] + dungeon[5-1] + "\n" +dungeon[5-1] + "---" + passages[getRandomInt(6)] + dungeon[6-1] + "\n" +dungeon[6-1] + "---" + passages[getRandomInt(6)] + dungeon[2-1] + "\n",
];
  var mermaidDiagramContainer = document.querySelector('#mermaid');
  return `
	\<pre class="mermaid"\>
    graph TD
    ` + dungeonLayouts[getRandomInt(13)] +
  `\<\/pre\>`;
 };

$(".add-diagram").on("click", function() {
  $("#output").html(dngngn());
  $("pre.mermaid").each(function(i, e) {
    // creating the container ID -> this is NOT unique, so if you have
    // multiple places on your page rendering multiple diagrams, you
    // should update this to be really unique
    const containerId = `mermaid-${i}`;

    // rendering the diagram with the unique ID
    renderDiagram(containerId, e);
  });
  // activates convert to png button
  const serializeAsXML = $e => (new XMLSerializer()).serializeToString($e)

  const encodeAsUTF8 = s => `${dataHeader},${encodeURIComponent(s)}`
  const encodeAsB64 = s => `${dataHeader};base64,${btoa(s)}`

  const convertSVGtoImg = async e => {
    const $btn = e.target
    const format = $btn.dataset.format ?? 'png'
    // $label.textContent = format

    destroyChildren($holder)

    const svgData = encodeAsUTF8(serializeAsXML($svg))

    const img = await loadImage(svgData)
    
    const $canvas = document.createElement('canvas')
    $canvas.width = $svg.clientWidth
    $canvas.height = $svg.clientHeight
    $canvas.getContext('2d').drawImage(img, 0, 0, $svg.clientWidth, $svg.clientHeight)
    
    const dataURL = await $canvas.toDataURL(`image/${format}`, 1.0)
    console.log(dataURL)
    
    const $img = document.createElement('img')
    $img.src = dataURL
    $holder.appendChild($img)
  }

  const buttons = [...document.querySelectorAll('[data-format]')]
  for (const $btn of buttons) {
    $btn.onclick = convertSVGtoImg
  }
})

All Posts