Advertisement
RefresherTowel

Labyrinth Proc Gen in GML

Sep 15th, 2017 (edited)
1,530
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2. Firstly, there needs to be 3 objects:
  3.  
  4. labyrinth_generation (the name doesn't matter for this one)
  5. obj_room (this name DOES matter)
  6. obj_corridor (so does this one!)
  7.  
  8. obj_room and obj_corridor should both have the same sprite; a filled in square that is the same
  9. size as your WALL_SIZE macro (check below). So for mine, the sprite is a 64x64 white square, if
  10. you use 32x32 as a grid size, you should make it a 32x32 white square. labyrinth_generation doesn't
  11. need a sprite. You can set these objects to invisible if you are simply using the grid for your
  12. labyrinth. No code is necessary in obj_room or obj_corridor.
  13.  
  14. You also have to set 3 (three) macros:
  15.  
  16. WALL_SIZE
  17. FLOOR
  18. WALL
  19.  
  20. Set WALL_SIZE equal to whatever you want your grid size to be (mine is 64x64), FLOOR equal
  21. to whatever value you want the grid to use as a floor and WALL equal to whatever value you
  22. want your grid to use as a wall.
  23.  
  24. Finally, you will need 3 (three) scripts:
  25.  
  26. generate_rooms()
  27. generate_corridor()
  28. rectangle_intersect()
  29.  
  30. Those names matter so keep them, unless you edit the code. Paste the first section of code below into
  31. the Create Event of your generation object (labyrinth_generation in this example). Below that are the
  32. scripts for generate_rooms(), generate_corridor() and rectangle_intersect(). This work is licensed under
  33. a Creative Commons Attribution 3.0 Australia License (https://creativecommons.org/licenses/by/3.0/au/). =D
  34.  
  35. -RefresherTowel
  36. */
  37.  
  38.  
  39. /*************************************************/
  40. /***** GENERATION OBJECT CREATION CODE START *****/
  41. /*************************************************/
  42.  
  43. randomize();
  44.  
  45. //Set the width and height of the room
  46. room_width = WALL_SIZE*150;
  47. room_height = WALL_SIZE*150;
  48.  
  49. //Set the width and height of the grid
  50. grid_width = room_width/WALL_SIZE;
  51. grid_height = room_height/WALL_SIZE;
  52.  
  53. arena = ds_grid_create(grid_width,grid_height);
  54. ds_grid_clear(arena,WALL);
  55.  
  56. //Start room position variation
  57. var start_random = 10;
  58. //X and y of the first room coordinates
  59. start[0] = round(grid_width/2)-irandom(start_random/2)+irandom(start_random);
  60. start[1] = round(grid_height/2)-irandom(start_random/2)+irandom(start_random);
  61.  
  62. //Corridor min and max length & width
  63. corridor_length[0] = 5;
  64. corridor_length[1] = 15;
  65. corridor_width[0] = 2;
  66. corridor_width[1] = 6;
  67.  
  68. //Room min and max size
  69. rm_size[0] = 10;
  70. rm_size[1] = 45;
  71.  
  72. //Min and max number of rooms
  73. rm_max_no[0] = 3;
  74. rm_max_no[1] = 10;
  75.  
  76. //Make sure at least 1 room is generated
  77. if (rm_max_no[0] < 1) {
  78.     rm_max_no[0] = 1;
  79. }
  80. if (rm_max_no[1] < 1) {
  81.     rm_max_no[1] = 1;
  82. }
  83.  
  84. //Actual number of rooms and corridors
  85. rm_no = irandom_range(rm_max_no[0],rm_max_no[1]);
  86. corridor_no = rm_no-1;
  87.  
  88. //Allow rooms to overlap?
  89. overlap = false;
  90.  
  91. generate_rooms();
  92.  
  93. /*************************************************/
  94. /****** GENERATION OBJECT CREATION CODE END ******/
  95. /*************************************************/
  96.  
  97.  
  98.  
  99.  
  100. /*************************************************/
  101. /****** generate_rooms() SCRIPT CODE START *******/
  102. /*************************************************/
  103.  
  104. ///generate_rooms();
  105.  
  106. //Make sure the arrays don't hold data
  107. rm = noone;
  108. corridor = noone;
  109. pos = noone;
  110. room_array = noone;
  111.  
  112. //Initialise the counter (to break the for if it can't generate a room)
  113. var c = 0;
  114.  
  115. //Loop through the target number of rooms to create them
  116. for(var i=0;i<rm_no;i++) {
  117.     //Starting room
  118.     if (i == 0) {
  119.         //Create the room and store it in the rm array
  120.         rm[i] = instance_create(start[0]*WALL_SIZE,start[1]*WALL_SIZE,obj_room);
  121.        
  122.         //Choose the width and height
  123.         var width = irandom_range(rm_size[0],rm_size[1]);
  124.         var height = irandom_range(rm_size[0],rm_size[1]);
  125.        
  126.         //Store the room properties in an array to be checked against if overlapping is set to false
  127.         room_array[i,0] = start[0];
  128.         room_array[i,1] = start[1];
  129.         room_array[i,2] = width;
  130.         room_array[i,3] = height;
  131.        
  132.         //Set the room to the correct width and height
  133.         rm[i].width = width;
  134.         rm[i].height = height;
  135.         rm[i].image_xscale = width;
  136.         rm[i].image_yscale = height;
  137.        
  138.         //Generate the corridor for the room
  139.         pos = generate_corridor(i,start,width,height,-1); //-1 for the first room direction
  140.        
  141.         /*
  142.         The pos variable is now an array containing 5 elements: the grid x and grid y coordinates of the next room,
  143.         the direction from the next room to the corridor that leads into it and the width and height of the next room
  144.         (pos[0],pos[1],pos[2],pos[3],pos[4]). We will use this to create the new room in the next loop.
  145.         */
  146.        
  147.         //Give the room a number; not used in this generation script but might be useful for something else
  148.         rm[i].no = i;
  149.        
  150.         //Set the arena grid inside the room to FLOOR
  151.         for (var xx=start[0];xx<start[0]+width;xx++) {
  152.             for (var yy=start[1];yy<start[1]+height;yy++) {
  153.                 arena[# xx,yy] = FLOOR;
  154.             }
  155.         }
  156.     }
  157.     //Other rooms
  158.     else if (i < rm_no-1) {
  159.         rm[i] = instance_create(pos[0]*WALL_SIZE,pos[1]*WALL_SIZE,obj_room);
  160.         //Get the width and height for the room, which was generated in the previous corridor
  161.         var width = pos[3];
  162.         var height = pos[4];
  163.         //Store room properties for overlapping check
  164.         room_array[i,0] = pos[0];
  165.         room_array[i,1] = pos[1];
  166.         room_array[i,2] = width;
  167.         room_array[i,3] = height;
  168.         //Set the width and height
  169.         rm[i].width = width;
  170.         rm[i].height = height;
  171.         rm[i].image_xscale = width;
  172.         rm[i].image_yscale = height;
  173.        
  174.         /*
  175.         If the room is outside the grid bounds (this should never happen!)
  176.         the gen will restart
  177.         */
  178.         if (pos[0] < 1 || pos[0]+width > grid_width-1 || pos[1] < 1 || pos[1]+height > grid_height-1 || place_meeting(x,y,obj_room)) {
  179.             show_debug_message("Room outside bounds, restarting!");
  180.             room_restart();
  181.             break;
  182.         }  
  183.         else {
  184.             //Give the room a number and generate its corridor
  185.             rm[i].no = i;
  186.             //Set the arena grid inside the room to FLOOR
  187.             for (var xx=pos[0];xx<pos[0]+width;xx++) {
  188.                 for (var yy=pos[1];yy<pos[1]+height;yy++) {
  189.                     arena[# xx,yy] = FLOOR;
  190.                 }
  191.             }
  192.             //Generate the new corridor
  193.             pos = generate_corridor(i,pos,width,height,pos[2]);
  194.             //If a new corridor can't be generated then stop
  195.             if (pos == 0) {
  196.                 show_debug_message("Too many rooms, stopping generation");
  197.                 break;
  198.             }
  199.         }
  200.     }
  201.     //Generate the last room
  202.     else {
  203.         rm[i] = instance_create(pos[0]*WALL_SIZE,pos[1]*WALL_SIZE,obj_room);
  204.         //Width and height from corridor
  205.         var width = pos[3];
  206.         var height = pos[4];
  207.         //Store room properties for overlapping check
  208.         room_array[i,0] = pos[0];
  209.         room_array[i,1] = pos[1];
  210.         room_array[i,2] = width;
  211.         room_array[i,3] = height;
  212.         //Set the width and height
  213.         rm[i].width = width;
  214.         rm[i].height = height;
  215.         rm[i].image_xscale = width;
  216.         rm[i].image_yscale = height;
  217.         //Restart if outside bounds (should never happen!)
  218.         if (pos[0] < 1 || pos[0]+width > grid_width-1 || pos[1] < 1 || pos[1]+height > grid_height-1 || place_meeting(x,y,obj_room)) {
  219.             show_debug_message("Room outside bounds, restarting!");
  220.             room_restart();
  221.             break;
  222.         }
  223.         else {
  224.             rm[i].no = i;
  225.             //Set the arena grid inside the room to FLOOR
  226.             for (var xx=pos[0];xx<pos[0]+width;xx++) {
  227.                 for (var yy=pos[1];yy<pos[1]+height;yy++) {
  228.                     arena[# xx,yy] = FLOOR;
  229.                 }
  230.             }
  231.         }
  232.     }
  233.     //If too many iterations have happened without being able to generate a room break the loop
  234.     if (c < 1000) {
  235.         c++;
  236.     }
  237.     else {
  238.         show_debug_message("Cant generate a room, stopping!");
  239.         break;
  240.     }
  241. }
  242.  
  243. /*************************************************/
  244. /******* generate_rooms() SCRIPT CODE END ********/
  245. /*************************************************/
  246.  
  247.  
  248.  
  249.  
  250. /*************************************************/
  251. /***** generate_corridor() SCRIPT CODE START *****/
  252. /*************************************************/
  253.  
  254. ///generate_corridor(corridor_no,pos,room_width,room_height,old_corridor_dir);
  255. var c_no = argument0;
  256. var r_pos = argument1;
  257. var r_width = argument2;
  258. var r_height = argument3;
  259. var p_dir = argument4;
  260.  
  261. //Generate a new corridor if the corridor limit hasn't been reached
  262. if (c_no < corridor_no) {
  263.  
  264.     //Set a random direction
  265.     dir = irandom(3);
  266.    
  267.     //If the random direction goes back along the previous generated corridor, pick a new direction
  268.     if (dir == p_dir) {
  269.         dir = (dir+1) mod 4;
  270.     }
  271.    
  272.     //Check to see if the corridor + new room will fit
  273.     var fits = false;
  274.     var attempts = 0;
  275.     while (fits == false) {
  276.         //Pick a length for the corridor
  277.         var length = irandom_range(corridor_length[0],corridor_length[1]);
  278.         var c_width = irandom_range(corridor_width[0],corridor_width[1]);
  279.         //Pick a width and height for the room at the end of the new corridor
  280.         var new_width = irandom_range(rm_size[0],rm_size[1]);
  281.         var new_height = irandom_range(rm_size[0],rm_size[1]);
  282.         //Cycle through the potential directions, finding the coordinates for the new room
  283.         switch (dir) {
  284.             case 0:
  285.                 new_x = r_pos[0]+r_width;
  286.                 new_y = r_pos[1]+irandom(r_height-1);
  287.                
  288.                 //Make the corridor move to try to fit the width without spilling over the side
  289.                 while (new_y+c_width > r_pos[1]+r_height-1) {
  290.                     new_y--;
  291.                     if (new_y <= r_pos[1]) {
  292.                         new_y = r_pos[1];
  293.                         break;
  294.                     }
  295.                 }
  296.                
  297.                 //If overlapping is allowed
  298.                 if (overlap) {
  299.                     //If the corridor or new room goes outside the grid boundary, pick a new direction to branch into
  300.                     if (new_x+length+new_width > grid_width-1 || new_y < 1 || new_y+new_height > grid_height-1) {
  301.                         dir = (dir+1) mod 4;
  302.                     }
  303.                     //Otherwise it fits
  304.                     else {
  305.                         fits = true;
  306.                     }
  307.                 }
  308.                
  309.                 //If overlapping isn't allowed
  310.                 else {
  311.                     //Check against the boundaries
  312.                     if (new_x+length+new_width > grid_width-1 || new_y < 1 || new_y+new_height > grid_height-1) {
  313.                         dir = (dir+1) mod 4;
  314.                     }
  315.                    
  316.                     //Otherwise check against overlap
  317.                     else {
  318.                         var rooms_cleared = 0;
  319.                         for (var k=0;k<instance_number(obj_room);k++) {
  320.                             if (rectangle_intersect(new_x+length,room_array[k,0],new_y,room_array[k,1],new_width,room_array[k,2],new_height,room_array[k,3])) {
  321.                                 //Try a new direction if current direction leads to an overlap
  322.                                 dir = (dir+1) mod 4;
  323.                                 fits = false;
  324.                                 break;
  325.                             }
  326.                             else {
  327.                                 //Add to the rooms cleared variable for every room that is checked and doesn't overlap
  328.                                 rooms_cleared++;
  329.                             }
  330.                         }
  331.                        
  332.                         //If all the rooms are clear, then the new room will fit, otherwise it won't
  333.                         if (rooms_cleared == instance_number(obj_room)) {
  334.                             fits = true;
  335.                         }
  336.                         else {
  337.                             fits = false;
  338.                         }
  339.                     }
  340.                 }
  341.             break;
  342.            
  343.             //As above but for a new direction
  344.             case 1:
  345.                 new_x = r_pos[0]+irandom(r_width-1);
  346.                 new_y = r_pos[1];
  347.                 while (new_x+c_width > r_pos[0]+r_width-1) {
  348.                     new_x--;
  349.                     if (new_x <= r_pos[0]) {
  350.                         new_x = r_pos[0];
  351.                         break;
  352.                     }
  353.                 }
  354.                 if (overlap) {
  355.                     if (new_y-length-new_height < 1 || new_x < 1 || new_x+new_width > grid_width-1) {
  356.                         dir = (dir+1) mod 4;
  357.                     }
  358.                     else {
  359.                         fits = true;
  360.                     }
  361.                 }
  362.                 else {
  363.                     if (new_y-length-new_height < 1 || new_x < 1 || new_x+new_width > grid_width-1) {
  364.                         dir = (dir+1) mod 4;
  365.                     }
  366.                     else {
  367.                         var rooms_cleared = 0;
  368.                         for (var k=0;k<instance_number(obj_room);k++) {
  369.                             if (rectangle_intersect(new_x,room_array[k,0],new_y-length-new_height,room_array[k,1],new_width,room_array[k,2],new_height,room_array[k,3])) {
  370.                                 dir = (dir+1) mod 4;
  371.                                 fits = false;
  372.                                 break;
  373.                             }
  374.                             else {
  375.                                 rooms_cleared++;
  376.                             }
  377.                         }
  378.                         if (rooms_cleared == instance_number(obj_room)) {
  379.                             fits = true;
  380.                         }
  381.                         else {
  382.                             fits = false;
  383.                         }
  384.                     }
  385.                 }
  386.             break;
  387.            
  388.             //As above but for a new direction
  389.             case 2:
  390.                 var new_x = r_pos[0];
  391.                 var new_y = r_pos[1]+irandom(r_height-1);
  392.                 while (new_y+c_width > r_pos[1]+r_height-1) {
  393.                     new_y--;
  394.                     if (new_y <= r_pos[1]) {
  395.                         new_y = r_pos[1];
  396.                         break;
  397.                     }
  398.                 }
  399.                 if (overlap) {
  400.                     if (new_x-length-new_width < 1 || new_y < 1 || new_y+new_height > grid_height-1) {
  401.                         dir = (dir+1) mod 4;
  402.                     }
  403.                     else {
  404.                         fits = true;
  405.                     }
  406.                 }
  407.                 else {
  408.                     if (new_x-length-new_width < 1 || new_y < 1 || new_y+new_height > grid_height-1) {
  409.                         dir = (dir+1) mod 4;
  410.                     }
  411.                     else {
  412.                         var rooms_cleared = 0;
  413.                         for (var k=0;k<instance_number(obj_room);k++) {
  414.                             if (rectangle_intersect(new_x-length-new_width,room_array[k,0],new_y,room_array[k,1],new_width,room_array[k,2],new_height,room_array[k,3])) {
  415.                                 dir = (dir+1) mod 4;
  416.                                 fits = false;
  417.                                 break;
  418.                             }
  419.                             else {
  420.                                 rooms_cleared++;
  421.                             }
  422.                         }
  423.                         if (rooms_cleared == instance_number(obj_room)) {
  424.                             fits = true;
  425.                         }
  426.                         else {
  427.                             fits = false;
  428.                         }
  429.                     }
  430.                 }
  431.             break;
  432.            
  433.             //As above but for a new direction
  434.             case 3:
  435.                 new_x = r_pos[0]+irandom(r_width-1);
  436.                 new_y = r_pos[1]+r_height;
  437.                 while (new_x+c_width > r_pos[0]+r_width-1) {
  438.                     new_x--;
  439.                     if (new_x <= r_pos[0]) {
  440.                         new_x = r_pos[0];
  441.                         break;
  442.                     }
  443.                 }
  444.                 if (overlap) {
  445.                     if (new_y+length+new_height > grid_height-1 || new_x < 1 || new_x+new_width > grid_width-1) {
  446.                         dir = (dir+1) mod 4;
  447.                     }
  448.                     else {
  449.                         fits = true;
  450.                     }
  451.                 }
  452.                 else {
  453.                     if (new_y+length+new_height > grid_height-1 || new_x < 1 || new_x+new_width > grid_width-1) {
  454.                         dir = (dir+1) mod 4;
  455.                     }
  456.                     else {
  457.                         var rooms_cleared = 0;
  458.                         for (var k=0;k<instance_number(obj_room);k++) {
  459.                             if (rectangle_intersect(new_x,room_array[k,0],new_y+length,room_array[k,1],new_width,room_array[k,2],new_height,room_array[k,3])) {
  460.                                 dir = (dir+1) mod 4;
  461.                                 fits = false;
  462.                                 break;
  463.                             }
  464.                             else {
  465.                                 rooms_cleared++;
  466.                             }
  467.                         }
  468.                         if (rooms_cleared == instance_number(obj_room)) {
  469.                             fits = true;
  470.                         }
  471.                         else {
  472.                             fits = false;
  473.                         }
  474.                     }
  475.                 }
  476.             break;
  477.         }
  478.        
  479.         //If too many attempts have been made to place a room, then it is impossible, so end the loop
  480.         if (attempts < 1000) {
  481.             attempts++;
  482.         }
  483.         else {
  484.             break;
  485.         }
  486.     }
  487.     //If the new corridor and room fit inside the grid
  488.     if (fits) {
  489.         //Generate the corridor and give it a number; the number isn't used in the script but it might be useful for something else
  490.         corridor[c_no] = instance_create(new_x*WALL_SIZE,new_y*WALL_SIZE,obj_corridor);
  491.         corridor[c_no].no = c_no;
  492.         //Scale the corridor according to the direction it is branching in
  493.         switch (dir) {
  494.             case 0:
  495.                 corridor[c_no].image_xscale = length;
  496.                 corridor[c_no].image_yscale = c_width;
  497.                 //Set the new rooms starting coordinates
  498.                 c_pos[0] = new_x+length;
  499.                 c_pos[1] = new_y;
  500.                 /*
  501.                 Set the arena grid inside the corridor to FLOOR (you could create a new macro
  502.                 and set this to something else if you wanted to have the rooms and corridors
  503.                 be distinct
  504.                 */
  505.                 for (var xx=new_x;xx<new_x+length+1;xx++) {
  506.                     for (var yy=new_y;yy<new_y+c_width;yy++) {
  507.                         arena[# xx,yy] = FLOOR;
  508.                     }
  509.                 }
  510.             break;
  511.            
  512.             //As above for a different direction
  513.             case 1:
  514.                 corridor[c_no].image_yscale = -length;
  515.                 corridor[c_no].image_xscale = c_width;
  516.                 c_pos[0] = new_x;
  517.                 c_pos[1] = new_y-length-new_height;
  518.                 for (var yy=new_y;yy>new_y-length-1;yy--) {
  519.                     for (var xx=new_x;xx<new_x+c_width;xx++) {
  520.                         arena[# xx,yy] = FLOOR;
  521.                     }
  522.                 }
  523.             break;
  524.            
  525.             //As above for a different direction
  526.             case 2:
  527.                 corridor[c_no].image_xscale = -length;
  528.                 corridor[c_no].image_yscale = c_width;
  529.                 c_pos[0] = new_x-length-new_width;
  530.                 c_pos[1] = new_y;
  531.                 for (var xx=new_x;xx>new_x-length-1;xx--) {
  532.                     for (var yy=new_y;yy<new_y+c_width;yy++) {
  533.                         arena[# xx,yy] = FLOOR;
  534.                     }
  535.                 }
  536.             break;
  537.            
  538.             //As above for a different direction
  539.             case 3:
  540.                 corridor[c_no].image_yscale = length;
  541.                 corridor[c_no].image_xscale = c_width;
  542.                 c_pos[0] = new_x;
  543.                 c_pos[1] = new_y+length;
  544.                 for (var yy=new_y;yy<new_y+length+1;yy++) {
  545.                     for (var xx=new_x;xx<new_x+c_width;xx++) {                    
  546.                         arena[# new_x,yy] = FLOOR;
  547.                     }
  548.                 }
  549.             break;
  550.         }
  551.        
  552.         /*
  553.         Let the new room know which direction corridor that creates it is so we
  554.         can make sure the next corridor doesn't go back in the same direction
  555.         */
  556.         c_pos[2] = (dir+2) mod 4;
  557.        
  558.         //Pass through the width and height of the next room
  559.         c_pos[3] = new_width;
  560.         c_pos[4] = new_height;
  561.        
  562.         /*
  563.         Return an array containing the grid x and y coordinates c_pos[0] & c_pos[1] for the next room,
  564.         the direction of the old corridor c_pos[2] and the width and height c_pos[3] & c_pos[4] for the
  565.         next room, so we can use them to generate it
  566.         */
  567.         return c_pos;          
  568.     }
  569. }
  570.  
  571. /*************************************************/
  572. /****** generate_corridor() SCRIPT CODE END ******/
  573. /*************************************************/
  574.  
  575.  
  576.  
  577.  
  578. /*************************************************/
  579. /**** rectangle_intersect() SCRIPT CODE START ****/
  580. /*************************************************/
  581.  
  582. ///rectangle_intersect(rect1_x,rect2_x,rect1_y,rect2_y,rect1_width,rect2_width,rect1_height,rect2_height);
  583. //Adapted from a mozilla code fragment with the help of Sidorakh
  584. var rect1_x,rect2_x,rect1_y,rect2_y,rect1_width,rect2_width,rect1_height,rect2_height;
  585. rect1_x = argument0-1;
  586. rect2_x = argument1-1;
  587. rect1_y = argument2-1;
  588. rect2_y = argument3-1;
  589. rect1_width = argument4+1;
  590. rect2_width = argument5+1;
  591. rect1_height = argument6+1;
  592. rect2_height = argument7+1;
  593.  
  594. if (rect1_x < rect2_x + rect2_width &&
  595.    rect1_x + rect1_width > rect2_x &&
  596.    rect1_y < rect2_y + rect2_height &&
  597.    rect1_height + rect1_y > rect2_y) {
  598.     return true;
  599. }
  600. else {
  601.     return false;
  602. }
  603.  
  604. /*************************************************/
  605. /***** rectangle_intersect() SCRIPT CODE END *****/
  606. /*************************************************/
  607.  
  608. //And you're done!
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement