Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

[Thinking about] Random Dungeon Generator with Dynamic Lighting

January 16 (11 years ago)

Edited January 16 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter


I made these straight forward simple tiles for an API Random Dungeon Generator with Dynamic Lighting.

They can be found on the market tag "oldstone."



The idea would be:
1) Size of the map ("!dungeon 100x100")
2) Place random rooms (based on map size and rules to leave room for halls.)
3) Place entry and exit to the dungeon level (manual or auto)
4) Connect passages (auto)
5) User edit
6) Auto draw dynamic lines ("!lightinglines")

These are very lightweight (only decorations are PNG and very few decoration, doors for example are still JPG and PNG.)

Wide walls allow for clear visible walls on both side with dynamic lighting (Can't stand that myself.)

AND.... the entire layout can be coded in an array because it is a grid system... so you can test for adjacent walls or stand alone columns.


OR.....

Thinking of making a cursor system and token actions as the tool bar for building the dungeon (this would be easy to take on.)


January 16 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
var rows = 50;
var cols = 50; on("ready", function() {
on("chat:message", function(msg) {
if(msg.type == "api"){processMessage(msg)};
});
}); processMessage = function(msg) {
if(msg.type !== "api"){return}; //Creates Map Array
var mapArray = matrix( rows, cols, "-");
}; function matrix( rows, cols, defaultValue){
var tempArray = [];
for(var i=0; i < rows; i++){
tempArray.push([]);
tempArray[i].push( new Array(cols));
for(var j=0; j < cols; j++){
tempArray[i][j] = defaultValue;
};
};
return tempArray;
};

This give an array for each "square" on that map...

Next would be an algorithm to define were the rooms are... passage ways... etc.

Once the mapArray is fully defined you loop through and populate wiht the images.

January 17 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
Example room generation:

var cornerTile = "https://s3.amazonaws.com/files.d20.io/images/2766641/BQFOkdBneZUFnQUkFORrsA/thumb.jpg?1389918058";
var roomArrays = [
{roomType: "fourBYfour", srcImg: cornerTile, hFlip: true, vFlip: false, padLeft: 0, padTop: 0 },
{roomType: "fourBYfour", srcImg: cornerTile, hFlip: false, vFlip: false, padLeft: 140, padTop: 0 },
{roomType: "fourBYfour", srcImg: cornerTile, hFlip: true, vFlip: true, padLeft: 0, padTop: 140 },
{roomType: "fourBYfour", srcImg: cornerTile, hFlip: false, vFlip: true, padLeft: 140, padTop: 140 },
{roomType: "sixBYsix", srcImg: cornerTile, hFlip: true, vFlip: false, padLeft: 0, padTop: 0 },
{roomType: "sixBYsix", srcImg: cornerTile, hFlip: false, vFlip: false, padLeft: 280, padTop: 0 },
{roomType: "sixBYsix", srcImg: cornerTile, hFlip: true, vFlip: true, padLeft: 0, padTop: 280 },
{roomType: "sixBYsix", srcImg: cornerTile, hFlip: false, vFlip: true, padLeft: 280, padTop: 280 },
{roomType: "sixBYfour", srcImg: cornerTile, hFlip: true, vFlip: false, padLeft: 0, padTop: 0 },
{roomType: "sixBYfour", srcImg: cornerTile, hFlip: false, vFlip: false, padLeft: 280, padTop: 0 },
{roomType: "sixBYfour", srcImg: cornerTile, hFlip: true, vFlip: true, padLeft: 0, padTop: 140 },
{roomType: "sixBYfour", srcImg: cornerTile, hFlip: false, vFlip: true, padLeft: 280, padTop: 140 },
];

The units are 70x70 squares... so "fourBYfour" = four 70px squares by four 70px square.. you just define the library of rooms the generator can select from.

You call a room into existence with:

buildRoom(0,0,"fourBYfour"); 
Where the zeros are the x and y for the map in squares starting with 0,0 being the top left square.

The function is as follows.

buildRoom = function(leftValue, topValue, roomType) {
var lv = (leftValue * 70) + 140;
var tv = (topValue * 70) + 140;
_.each(roomArrays, function(indexRooms) {
if(indexRooms.roomType == roomType){
createObj("graphic", {_type: "graphic",_subtype: "token", _pageid: pageId, layer: "map", width: 280, height: 280,
left: lv + indexRooms.padLeft,
top: tv + indexRooms.padTop,
imgsrc: indexRooms.srcImg,
flipv: indexRooms.vFlip,
fliph: indexRooms.hFlip,
});
};
});
};


January 17 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter

Generating basic rooms shapes without doors (to be added later.)

And captures any walls created in the array:

######-########-##########-############-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
######-########-##########-############-----------
--------------------------------------------------
######-########-##########-############-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
######-########-##########-############-----------
--------------------------------------------------
######-########-##########-############-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
######-########-##########-############-----------
--------------------------------------------------
######-########-##########-############-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
#----#-#------#-#--------#-#----------#-----------
######-########-##########-############-----------
--------------------------------------------------
--------------------------------------------------
--------------------------------------------------
--------------------------------------------------
--------------------------------------------------
--------------------------------------------------
--------------------------------------------------
--------------------------------------------------
--------------------------------------------------
--------------------------------------------------
--------------------------------------------------

The array is needed for placement of rooms, corridors, doors, columns and dynamic lighting lines.

Now to work on random room placement:
1) has to be so far from the edge of the map.
2) has to be so far from an already existing room (to leave room for corridors.)
3) can not consume some percentage of the map in total with all other rooms.

After that its random door opening placement
1) one entry to the room or two or more
2) which side or sides
3) door, passage, portcullis or secret door
January 17 (11 years ago)
I'd like to share a link:
http://adom.de/

Read some of his blog posts, and really any information about various roguelikes for ideas on how maps might be generated. I know there was a big ordeal over at Dungeon Crawl Stone Soup when they added the Shoals, because they wanted a map generator that would feel like beaches. They eventually did it by generating a map with rectangles like yours, then running a filter to smooth them a few times. This is easily modified to make caves.

If you happen to be into programming at all, I would bet roguelikes hold a whole bunch of hidden gems that could be used in scripting on roll20.
while you're looking at them, hit crawl.develz.org, and play some.
January 18 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter

Steve S. said:

I'd like to share a link:
http://adom.de/

Read some of his blog posts, and really any information about various roguelikes for ideas on how maps might be generated. I know there was a big ordeal over at Dungeon Crawl Stone Soup when they added the Shoals, because they wanted a map generator that would feel like beaches. They eventually did it by generating a map with rectangles like yours, then running a filter to smooth them a few times. This is easily modified to make caves.

If you happen to be into programming at all, I would bet roguelikes hold a whole bunch of hidden gems that could be used in scripting on roll20.
while you're looking at them, hit crawl.develz.org, and play some.

I will read through it... Pathing between rooms will be the heavy lifting...

..currently on...



..random room placement:
1) has to be so far from the edge of the map (CHECK)
2) has to be so far from an already existing room (to leave room for corridors.)
3) can not consume some percentage of the map in total with all other rooms (CHECK)

Working on item 2... logging the rooms...
[{"roomID":0,"atX":18,"atY":20,"typeRoom":"room8x4"},{"roomID":1,"atX":17,"atY":34,"typeRoom":"room6x8"},{"roomID":2,"atX":16,"atY":15,"typeRoom":"room4x6"},{"roomID":3,"atX":32,"atY":12,"typeRoom":"room6x6"},{"roomID":4,"atX":31,"atY":23,"typeRoom":"room6x6"},{"roomID":5,"atX":31,"atY":30,"typeRoom":"room4x6"},{"roomID":6,"atX":11,"atY":14,"typeRoom":"room6x8"},{"roomID":7,"atX":18,"atY":29,"typeRoom":"room4x6"},{"roomID":8,"atX":32,"atY":34,"typeRoom":"room8x4"},{"roomID":9,"atX":7,"atY":24,"typeRoom":"room4x6"},{"roomID":10,"atX":31,"atY":36,"typeRoom":"room6x6"},{"roomID":11,"atX":30,"atY":34,"typeRoom":"room4x6"},{"roomID":12,"atX":14,"atY":12,"typeRoom":"room4x8"}]
...just need to add a function to check for overlap... before the room is added (right now they just get added.)

Then play around with % map consumed and edge of the map on balance with number of rooms generated.

The mess in red to the right is the array spammed to the chat window.... which will capture doors and walls for the Dynamic Lighting...

January 18 (11 years ago)
Riley D.
Roll20 Team
This is pretty neat! Keep at it!
January 18 (11 years ago)
lmao, this looks like a classic ASCII adaptation to the java API. I love it. Once you wrap that up, if you can find an artist and code an old school angband remake, I'd ask the devs if you could sell the code and art on the market!
January 18 (11 years ago)

Edited January 18 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
The other cool thing about capturing the map in an array is things like pit traps on the GM layer... and the like.

You know when a token moves "off" map or on a trap or.... whatever.

You have the entire map features in an array to do whatever wonky or wacky thing you like.

I like many of the tile packs for sale, but most are "heavy" (PNG) or not dynamic lighting friendly (walls too thin to see the features from all angles) and don't leverage the game mechanic reality that if something is on a "square" that space is occupied by something a player should be able to interact with OR not want to interact with.

This design approach address all that... with also adding a half grid "line" or "+" to center and draw on for walls.
January 18 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
Also.... I am doing a "random generator"..... BUT you could "free build" and then "read" the map tokens with API to build the array of the complete map and then do things like auto draw dynamic lighting and capture traps placed on the GM layer.

Also if you look at the pack there are these:



Secret door and the old school one way secret door.... if you were nutting with automation those elves (and the like) could get auto checks for detecting secret door just by their token or way-points being near it.

The point is I guess.... map tokens should have an eye on game play too.
January 18 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter


Rooms done... add door and then corridors (<-ideas here would be most welcome.)
January 18 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
oldstoneDungeonGenerator = function(msg) {
//Define the total map area
roll20API.mapArea = roll20API.mapWidth * roll20API.mapHeight
//Build a blank arrary which represents the total map 0 through (mapWidth-1) by 0 through (mapHeight-1)
//So for a map 52 by 52 roll20API.mapArray[0][0] would be the upper left square...
//And roll20API.mapArray[51][51] would be the lower right square.
matrix(roll20API.mapHeight, roll20API.mapWidth, "0");

//numeric values are used so we can "sum" blocks of squares to detect objects.
//UTILITY FUNCTION - Not needed. It spams the roll20API.mapArray[][] to the chat window.
/* Turned off here
drawAscii();
*/
/*First part of the overal process
1) Random Room generation
a) has to be so far from the edge of the map.
b) can not consume some percentage of the map in total with all other rooms.
c) has to be so far from an already existing room (to leave room for corridors.)
*/
randomRoom();
};
//Build a blank arrary which represents the total map 0 through (mapWidth-1) by 0 through (mapHeight-1)
matrix = function(rows, cols, defaultValue){
roll20API.mapArray = [];
for(var i=0; i < rows; i++){roll20API.mapArray.push([]); roll20API.mapArray[i].push( new Array(cols));
for(var j=0; j < cols; j++){roll20API.mapArray[i][j] = defaultValue;};
};
};

//UTILITY FUNCTION - Not needed. It spams the roll20API.mapArray[][] to the chat window
drawAscii = function(rows, cols){
var asciiImage = "/direct <br><code><br>";
for(var i=0; i < rows; i++){
for(var j=0; j < cols; j++){asciiImage += roll20API.mapArray[i][j];};
asciiImage += "<br>"
};
sendChat("API", asciiImage + "</code>" )
};

/*First part of the overal process
1) Random Room generation
a) has to be so far from the edge of the map.
b) can not consume some percentage of the map in total with all other rooms.
c) has to be so far from an already existing room (to leave room for corridors.)
*/
randomRoom = function() {
//var limitRoomArea = Used to limit the total room area
//roll20API.spaceUsed = Running total of the area the room consume
//roll20API.rooms = Object that will contain information on each room generated
//var attemptsMap = Overall number of trys to place all the rooms (avoid infinite loops)
//var roomCount = Number of trys per room add effort (avoid infinite loops)
//var rightEdge = Defines the right side edge of the map for rooms
//var bottomEdge = Defines the bottom side edge of the map for rooms
var limitRoomArea = roll20API.mapArea * parseFloat(roll20API.limitRoomAreaPercent/100)
roll20API.spaceUsed = 0;
roll20API.rooms = [];
var attemptsMap = 0
var roomCount = 0
var rightEdge = parseInt(roll20API.mapWidth) - 7;
var bottomEdge = parseInt(roll20API.mapHeight) - 7;

//Do loop for the overall effort of placing all rooms
do{
attemptsMap ++
roll20API.validPlacement = false;
//if a) b) and c) are met this will toggle true and a room will be placed
var attemptsRoom = 0;

//Do loop for the indivdual room placement attempt
do{
attemptsRoom ++

//need both a numeric and string representation of room dimensions
var tryHdim = (Math.floor(Math.random() * 4) + 2) * 2; //will produce 4, 6, 8 or 10
var tryVdim = (Math.floor(Math.random() * 4) + 2) * 2; //will produce 4, 6, 8 or 10
var numtryHdim = tryHdim //numeric room dimension
var numtryVdim = tryVdim //numeric room dimension
if(tryHdim == 10){tryHdim = "0";}else{tryHdim.toString()}; //stringequal to "4","6","8" or "0"
if(tryVdim == 10){tryVdim = "0";}else{tryVdim.toString()}; //stringequal to "4","6","8" or "0"
var tryRoom = "room" + tryHdim + "x" + tryVdim; //string valid with roll20API.roomArrays (roomType: "room4x4")
var tryAtX = Math.floor(Math.random() * roll20API.mapWidth); //left location to try place a room
var tryAtY = Math.floor(Math.random() * roll20API.mapHeight); //top location to try place a room
var tryArea = numtryHdim * numtryVdim; //The area the room will consume
//If a) not near the edge of the map and b) does not comsume too much of the map...
//...then check for room inersection using the overlapCheck(x,y,Room) function
if(
tryAtX > 5 &&
(tryAtX+numtryHdim) < rightEdge &&
tryAtY > 5 &&
(tryAtY+numtryVdim) < bottomEdge &&
(roll20API.spaceUsed + tryArea) < limitRoomArea){

//Calls a function to check for room overlap
overlapCheck(tryAtX,tryAtY,tryRoom);
};

//Repeat the above placement attempt for a room until a valid placement is found...
//...or over the number of trys for a room
}while (roll20API.validPlacement == false && attemptsRoom < roll20API.attemptsRoomLimit);
//When and if a valid room placement is found: place the room, record the area and add the room to the array.
if(roll20API.validPlacement == true){
roll20API.spaceUsed += tryArea;
buildRoom(tryAtX,tryAtY,tryRoom);
roll20API.rooms.push({roomID: roomCount, atX: tryAtX, atY: tryAtY, typeRoom: tryRoom, Hdim: numtryHdim, Vdim: numtryVdim});
roomCount ++;
};
}while (attemptsMap < roll20API.attemptsMapLimit && roll20API.spaceUsed < limitRoomArea);

//UTILITY FUNCTION - Not needed. It spams the roll20API.mapArray[][] to the chat window.
drawAscii(roll20API.mapHeight, roll20API.mapWidth)
};

overlapCheck = function(leftValue, topValue, roomType) {
/*
x11,y11 = @----------------+
| |
| |
|x21,y21 = @-----+--------------+
| | | |
+----------+-----@ = x12,y12 |
| |
| |
+--------------------@ = x22,y22
*/
//roll20API.validPlacement = true for a) not near the edge of the map and b) does not comsume too much of the map
//This test for overlap based on "padded" room size to leave room for corridors
//leftValue (x11 unpadded) and topValue (y11 unpadded) are give.
//x12 and y12 are parse and padded based on the roomType.
var padSize = 4;
var x11 = leftValue - padSize;
var y11 = topValue - padSize;
var tryingXsecond
var tryingYsecond
if(parseInt(roomType.substr(4,1)) == 0){tryingXsecond = 10;}else{tryingXsecond = parseInt(roomType.substr(4,1))};
if(parseInt(roomType.substr(6,1)) == 0){tryingYsecond = 10;}else{tryingYsecond = parseInt(roomType.substr(6,1))};
var x12 = tryingXsecond + leftValue + padSize;
var y12 = tryingYsecond + topValue + padSize;
//If its the first room its ok to return and place
if(roll20API.rooms.length == 0){
roll20API.validPlacement = true;
return;

}else{
var overlap = false;
_.each(roll20API.rooms, function(indexRooms) {
//loop through the rooms that exist and get x21,y21,x22 and y22
var x21 = indexRooms.atX - padSize;
var y21 = indexRooms.atY - padSize;
var x22 = (indexRooms.atX + indexRooms.Hdim) + padSize
var y22 = (indexRooms.atY + indexRooms.Vdim) + padSize
//compares the "try" room with each room in the array
x_overlap = Math.max(0, Math.min(x12,x22) - Math.max(x11,x21));
y_overlap = Math.max(0, Math.min(y12,y22) - Math.max(y11,y21));
if ((x_overlap * y_overlap) != 0){overlap = true;};
});
if(overlap == false){roll20API.validPlacement = true;};
};
};

//This build the room and updates the map array
buildRoom = function(leftValue, topValue, roomType) {
//place the room on the map layer
var lv = (leftValue * 70) + 140;
var tv = (topValue * 70) + 140;
_.each(roll20API.roomArrays, function(indexRooms) {
if(indexRooms.roomType == roomType){
createObj("graphic", {_type: "graphic",_subtype: "token", _pageid: roll20API.pageId, layer: "map", width: 280, height: 280,
left: lv + indexRooms.padLeft,
top: tv + indexRooms.padTop,
imgsrc: indexRooms.srcImg,
flipv: indexRooms.vFlip,
fliph: indexRooms.hFlip,
rotation: indexRooms.rot,
});
};
});

/* what follows is coding the rooms into the map array
0=Open space
1=Near room (two square pad around the rooms)
3=Wall
5=space in a room:
Example:
000000000000000000000000000000000000000000000113555531100000
000001111111111000000000000000000000000000000113555531100000
000001111111111000000000000000000000000000000113555531100000
000001133333311000000000000000000000000000000113555531100000
000001135555311000000000000000000000000000000113333331100000
000001135555311000011111111111111110000000000111111111100000
000001135555311000011111111111111110000000000111111111100000
000001135555311000011333333333333110000000000000000000000000
000001135555311000011355555555553110000000000000000000000000
000001135555311000011355555555553110000111111111111110000000
000001133333311000011355555555553110000111111111111110000000
000001111111111000011355555555553110000113333333333110000000
000001111111111000011355555555553110000113555555553110000000
000000000000000000011355555555553110000113555555553110000000
000000000000000000011333333333333110000113555555553110000000
000000000000000000011111111111111110000113555555553110000000
000000000000000000011111111111111110000113333333333110000000
*/
//parse dimensions based on roomType
var hDim
var vDim
if(parseInt(roomType.substr(4,1)) == 0){hDim = 10;}else{hDim = parseInt(roomType.substr(4,1))};
if(parseInt(roomType.substr(6,1)) == 0){vDim = 10;}else{vDim = parseInt(roomType.substr(6,1))};
roll20API.spaceUsed += hDim * vDim;
for(var j=-2; j < hDim+4; j++){
roll20API.mapArray[topValue-1][leftValue + j] = "1";
};
for(var j=-2; j < hDim+4; j++){
roll20API.mapArray[topValue-2][leftValue + j] = "1";
};
roll20API.mapArray[topValue][leftValue - 1] = "1";
roll20API.mapArray[topValue][leftValue - 2] = "1";
roll20API.mapArray[topValue][leftValue + hDim + 2] = "1";
roll20API.mapArray[topValue][leftValue + hDim + 3] = "1";
for(var j=0; j < hDim+2; j++){
roll20API.mapArray[topValue][leftValue + j] = "3";
};
for(var i=1; i < vDim+1; i++){
roll20API.mapArray[topValue+i][leftValue-2] = "1";
roll20API.mapArray[topValue+i][leftValue-1] = "1";
roll20API.mapArray[topValue+i][leftValue] = "3";
roll20API.mapArray[topValue+i][leftValue+hDim+1] = "3";
roll20API.mapArray[topValue+i][leftValue+hDim+2] = "1";
roll20API.mapArray[topValue+i][leftValue+hDim+3] = "1";
};
for(var i=1; i < vDim+1; i++){
for(var j=1; j < hDim+1; j++){
roll20API.mapArray[topValue+i][leftValue + j] = "5";
};
};
for(var j=0; j < hDim+2; j++){
roll20API.mapArray[topValue+vDim+1][leftValue + j] = "3";
};
roll20API.mapArray[topValue+vDim+1][leftValue - 1] = "1";
roll20API.mapArray[topValue+vDim+1][leftValue - 2] = "1";
roll20API.mapArray[topValue+vDim+1][leftValue + hDim + 2] = "1";
roll20API.mapArray[topValue+vDim+1][leftValue + hDim + 3] = "1";
for(var j=-2; j < hDim+4; j++){
roll20API.mapArray[topValue+vDim+2][leftValue + j] = "1";
};
for(var j=-2; j < hDim+4; j++){
roll20API.mapArray[topValue+vDim+3][leftValue + j] = "1";
};
};
January 18 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
That is pretty much room placement (there are some arrays not shown... but that is the process I took... Working on recursive algorithm for the passage ways.
January 26 (11 years ago)

Edited January 26 (11 years ago)
Chad
Sheet Author
You might also want to look at this script here: http://donjon.bin.sh/fantasy/dungeon/about/dungeon...

Yes, it's in perl, but the writer has some good ideas in his generation sections.


January 26 (11 years ago)
Chad
Sheet Author
Oooh... this is simpler, but at least in JavaScript :) http://bigbadwofl.me/random-dungeon-generator/
January 27 (11 years ago)
This is cool. I'm actually working on a script right now that will convert polygons into rooms by evaluating them and placing appropriate floor and wall graphics. Hoping to be able to make helper tokens that live in the GM layer and can be used to move entire rooms around.
January 27 (11 years ago)
Chad
Sheet Author
I've started a JavaScript translate of the Perl script. It will take a while.... but it should work when done, at least to create an array of the map. I'm working on methods to actually transfer the map to the online game board.
January 27 (11 years ago)
Gold
Forum Champion
Keep up the cool work & ideas, thanks for sharing
January 30 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
Going in a different direction from when I started....

Going to go with geomorphic map tiles

Geomorphic map tiles are a design technique that defines and locates set features on edge of the tile so any tile can connect to any other tile without concern for orientation.

This map is a map 480 by 480 (massive) however its only 16 tiles:



The tiles have fixed connection points (some can be closed off solid or secret doors.)



You can also make "3D" corridors that cross over and under each other.



And the tiles will also connect "off center."



So 50 tiles in an array with door and wall locations could yield thousands of maps with very little coding.
January 30 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
geomorph = function() {
    _.each(frameArray, function(indexRooms) {
        createObj("graphic", {_type: "graphic",_subtype: "token", 
            _pageid: pageID, layer: "map", width: 840, height: 840,
            left: (indexRooms.column * 840) + 420, 
            top: (indexRooms.row * 840) + 420,  
            imgsrc: indexRooms.srcImg,
            flipv:  indexRooms.vFlip, 
            fliph:  indexRooms.hFlip, 
            rotation: indexRooms.rotation, 
        });
    }); 
    for (var i=1;i<5;i++){ 
        var randomRoom = roomArray[Math.floor(Math.random() * roomArray.length)].room;
        for (var j=1;j<5;j++){ 
            var randomRoom = roomArray[Math.floor(Math.random() * roomArray.length)].room;
            createObj("graphic", {_type: "graphic",_subtype: "token", 
                _pageid: pageID, layer: "map", width: 840, height: 840,
                left: (i * 840) + 420, 
                top: (j * 840) + 420,  
                imgsrc: randomRoom,
                flipv:  [true,false][Math.round(Math.random())], 
                fliph:  [true,false][Math.round(Math.random())], 
                rotation: [0,90][Math.round(Math.random())], 
            });
        };
    };
};

That little bit of code plus an array to frame the dungeon and an array of the geomorphic tiles is all that is needed to generate this:



If I add an array that provides "dynamic lighting" line information and possible door locations and types then two more loops and you have a one API command dungeon ready to go.

January 31 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
https://gist.github.com/BaldarSilveraxe/8725741

There is the gist...

And see the wiki on the image URLs... https://wiki.roll20.net/API:Objects#imgsrc_and_ava...

January 31 (11 years ago)
Chad
Sheet Author
I'm not sure anyone else can access your images, but it is definitely work a try! Thanks!

January 31 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter

Chad said:

I'm not sure anyone else can access your images, but it is definitely work a try! Thanks!




Anyone can access these ...They are on the market... Link

But the point is not this set of tokens..... its the geomorphic approach to tiles and the fact this old idea (geomorphs have been around since the 70's) really lends itself to the way Roll20 works.

Consider...



With clever way of encoding where doors can be (D) where walls are (any grid square with a green line the API could parse out possible door placements (to be treated as a short wall in another color on the DL layer 1/2 grid wider if a wall is placed.)

The API can also detect flat sides so the tile can be used as an outer edge tile (or corner tiles or entrance tiles.)

12 by 12 is a pretty small area... so only a finite number of playable tiles exist.

What market creators need is a standard for wall thickness and wall shadow drop (just for where the tiles connect)... the rest is whatever texture is applied and other elements added.

Two tiles could be "Type A" with wall/door string "X" and look very different... so there would be a set number of wall/door patterns.

If we could access the tags with API in our library... well, your random maps are limited only to what you tag as "Type A" and "WallDoors x"

February 06 (11 years ago)
I'm a bit disappointed that I purchased your other tileset in anticipation of the dungeon generator only to have it move on to a new tileset :/ Looks like I'll have to wait till I'm flush again.
February 06 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter

Demose said:

I'm a bit disappointed that I purchased your other tileset in anticipation of the dungeon generator only to have it move on to a new tileset :/ Looks like I'll have to wait till I'm flush again.

Check your PMs.
February 07 (11 years ago)
You're pretty much awesome and I feel like an entitled loser now :P I left out the part about how awesome and useful your tile set has been for me even without the generator, and that was rude of me!
February 07 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
Think nothing of it.

I actually hope geomorphs are added by other people that sell on the market place.

Let me know if you get the code and tiles working... would like to know what you think before I take the time to do the dynamic lighting.

February 07 (11 years ago)

Edited February 07 (11 years ago)
I'm not an API user, but I absolutely love the work your doing here. I may have to spend some time making Geomorph tiles in the future. Do you have plans for automating/randomizing placeables? Say, have the code designate a room as Kitchen/prisoner cell/treasure vault, or whatever and then place an object from the corresponding list.

Keep up the Awsome work. I imagine that once this catches on, your work will be a foundation for many adventures. +++
February 07 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
I would encourage just modify the tiles and making several types of "wine cellars" for example (or whatever.)



The basic wall structure doesn't change but this tile could be dozens of different rooms... wine cellar, prisoner cell block, armory.. etc.. and a secret door could be on any of the "closed off" passages... even if you seen the room before.. it may not be the same room at all.

Then the API would have broad rules like a wine cellar gets a "stairs up" tile next to it... and since it selected wine cellar that was a type A tile with wall pattern x... the dynamic lighting lines are easy.
I... I... This... This is just bloody beautiful. I am only just now starting to learn coding so what you have done here is nothing short of Magic brought to reality! Keep up the amazing work!
February 08 (11 years ago)
I love the idea of this. I really do. I am just not following exactly how to operate the script (i.e. how to find the current page information, though that is probably on the wiki somewhere), how to enter the image url into the code for the tiles you want to use (again, probably on the wiki), the activation command which would generate the random dungeons, and so on..) or the explanation of what is going on. Would it be possible to break down, step by step, how to utilize this effectively?
February 08 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
https://wiki.roll20.net/User:135636#Random_Dungeon...

Let me know if that helps or is not clear enough.
February 08 (11 years ago)

Stephen S. said:

https://wiki.roll20.net/User:135636#Random_Dungeon...

Let me know if that helps or is not clear enough.

The only thing I wasn't able to ascertain from that article (very clear and concise by the way, thank you for that) was how to obtain the page ID. I'm not sure how to utilize the Campaign object in API. I looked at the linked article but I wasn't really understanding it.

What I did get from it:
- You have to create your own chat command linked to the API to get it to generate a dungeon (I.e. !genmap).. though the specific code I'm not quite sure of. Looking at other scripts I see something like " if(msg.type == "api" && msg.content.indexOf("!clearInit") != -1 )" being used.
- You have to get the image URL from the specific tiles you want for your borders, and for your centers.. borders go in the first area with the corners and sides, and centers go in the array URL list (or is that incorrect?).
February 09 (11 years ago)

Edited February 09 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
I updated the instructions and added three commands.
!page with give you the page ID for the currently player page (useful for modify the code.)
!urls will give you the partial URLs needed for any image you place on the current player page.
!geo will make your map.

I suggest making one page for your random dungeon generator.... that way you never have to modify the code after that.... you can just duplicate the page and use the copy for play or even copy into another campaign using the Transmogrifier
February 09 (11 years ago)
That works beautifully Stephen! This is such an incredible script! How is the Dynamic Lighting portion coming along? I can see this is almost a finished product. I am excited!
February 09 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
It is kinda cool to play with.

Still thinking through the best way to do that.

Rob M. makes a good point after I reflect on it a bit... you want the API to know the empty space on the map too.

So the tile below...



Would likely need to be expressed in an array like this:

132231132231
300000000003
200000000002
200000000002
312211112213
100001000001
100001000001
300001112213
200000100001
200000100001
300000100003
131131131131

Where 1 is a wall, 3 is an tile connector (which should almost always be true except for special tiles), 2 is a possible door placement and 0 is open space.


What would be great is if API could read tags... that way that information could be in the tags.

If that was possible, the API could just search your library based on tags... looking for "geomorph" + "typeA" + "Dungeon" ( no need to hard code URLS any more) and the additional tags would be the array information.

You would just tags the tiles like below (the letters keep you from jumbling a row.)

[geomorph], [typeA], [Dungeon], [a132231132231], [b300000000003], [c200000000002], [d200000000002], [e312211112213], [f100001000001], [g100001000001], [h300001112213], [i200000100001], [j200000100001], [k300000100003], [l131131131131]

Add a new tile? Just update the tags for that tile... and your dungeon generator is current.



But not sure if Riley plans to add access to tags or maybe someone else has a better idea.
February 09 (11 years ago)
Well, that I actually understood.


So, without the ability for the API to read tags, currently you'd have to build an array for each tile you have in the script for the lighting, correct?
I would think that would be a lot of work to do.

Hopefully in a future update the tags for tiles will be able to be added to the API system. I kind of like the idea for more than just map making.. it'll help for monster tokens (such as a random encounter with monster tokens being added randomly to the map.. you could even include parameters for a certain number of minions, elite, solo, or standard monsters based on exp values as well.. that could be super useful.), generating random items or terrain on any given map, or even help with having evenly spaced light emitting tokens such as torches or campfires.
February 12 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
I was wrong.... let me rephrase that, I found a easy way to succeed in spite of Riley's efforts to confound me.

Rollable tables.

Gives you the URL without having to look up the URL manually (just build the table and the find the item in the table.)

And you can "name" the item... so its simple to build an array with dynamic lighting information that can be shared easily.

Rewriting things with that in mind and moving on the dynamic lighting along with adding tiles I excluded in the code (they are in the pack) that need to be somewhat considerate of the tiles to the left, right, above and below them (the train tracks tiles should try to extend once one randomly appears for example.)
February 12 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
The other good thing about this is you can build one campaign just for your random dungeon generator(s) and use the Transmogrifier to pull over the maps as needed.

February 12 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter


Frame is now also random... and when it switches to the "unfinished" tiles (on the right side) it tries to continue that type of tile.
February 12 (11 years ago)
I like that idea. Do you name the item in your table the same as the file name of the tile, or does it matter as long as the name of the tile is set to what it needs to be?

Also, with the upcoming lighting script, will that be written to correspond to the numbered side of the table.. And if so, will the order of the tiles matter in the table so that the paths affect the correct part of the map?

(sorry for all the questions.. Just trying to learn a little bit (: )
February 12 (11 years ago)

Edited February 12 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
Yes... you just add them to a Rollable Table using the normal Roll20 interface for building a table which is fairly pain free.



The file name becomes the relate-able part of the hard coded array (but that array can be shared without editing) and "order" does not matter at all.)

This code...

geomorphicTilesArray = new Array();
_.each(geomorphicTilesTableItems, function(geomorphicTilesTableEach) {
obj = _.find(geomorphicTilesInformation, function(obj) {return obj.tileName == geomorphicTilesTableEach.get("name");});
geomorphicTilesArray.push({
tileName: geomorphicTilesTableEach.get("name"),
tileURL: geomorphicTilesTableEach.get("avatar").replace("med.jpg?","thumb.jpg?"),
edge: obj.edge,
corner: obj.corner,
entrance: obj.entrance,
cave: obj.cave,
cornerRotation: obj.cornerRotation
});
});
...goes through each item in the Rollable Tables and builds an array with the URLs (which is part of the Item object) and relates to the hard coded array that has information about the tile (which again will never change and can be shared with any user without edit)... Information like: corner tile, edge tile, rarity of the tile, normal orientation of the tile, type of tile (cave, dungeon, etc), door placement, wall placement, special connection to adjacent tiles (sewer and rail tiles)... etc.

You could have different Rollable Tables for different set of geomorphic tiles.. or combined sets. You could have different maps too... and all of this API and such could be part of one stand alone campaign that you could use to pluck maps from using the Transmogrifier.

You could even have a process for manual map ... then "click" .... the API reads the map images and figures out how to draw the dynamic lines and your map is ready to copy over. (I thought of making a "cursor" tile with token actions to speed map building.)

February 12 (11 years ago)

Stephen S. said:

This code...

geomorphicTilesArray = new Array();
_.each(geomorphicTilesTableItems, function(geomorphicTilesTableEach) {
obj = _.find(geomorphicTilesInformation, function(obj) {return obj.tileName == geomorphicTilesTableEach.get("name");});
geomorphicTilesArray.push({
tileName: geomorphicTilesTableEach.get("name"),
tileURL: geomorphicTilesTableEach.get("avatar").replace("med.jpg?","thumb.jpg?"),
edge: obj.edge,
corner: obj.corner,
entrance: obj.entrance,
cave: obj.cave,
cornerRotation: obj.cornerRotation
});
});

So this would need to be added to the old Gist in order to update it for use with the rollable tables?


Stephen S. said:

...goes through each item in the Rollable Tables and builds an array with the URLs (which is part of the Item object) and relates to the hard coded array that has information about the tile (which again will never change and can be shared with any user without edit)... Information like: corner tile, edge tile, rarity of the tile, normal orientation of the tile, type of tile (cave, dungeon, etc), door placement, wall placement, special connection to adjacent tiles (sewer and rail tiles)... etc.

Where would that information be located? Is it part of the code, or is it something like tags that is being worked on now?

Stephen S. said:


You could even have a process for manual map ... then "click" .... the API reads the map images and figures out how to draw the dynamic lines and your map is ready to copy over. (I thought of making a "cursor" tile with token actions to speed map building.)


The dynamic line script is something that is in the works now, correct?
February 12 (11 years ago)
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
Yea. I am just working though it. I will post it all and update things.

Fairly close, just rewriting some of the code.
February 13 (11 years ago)
Sweet! Now to attempt that "patience" thing :D