Advertisement
neonblack

Random Dungeon Generator v1.0

Oct 13th, 2012
2,265
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 23.06 KB | None | 0 0
  1. ###--------------------------------------------------------------------------###
  2. #  CP Dungeon Generator script                                                 #
  3. #  Version 1.0                                                                 #
  4. #                                                                              #
  5. #      Credits:                                                                #
  6. #  Original code by: Neonblack                                                 #
  7. #  Modified by:                                                                #
  8. #                                                                              #
  9. #  This work is licensed under the Creative Commons Attribution-NonCommercial  #
  10. #  3.0 Unported License. To view a copy of this license, visit                 #
  11. #  http://creativecommons.org/licenses/by-nc/3.0/.                             #
  12. #  Permissions beyond the scope of this license are available at               #
  13. #  http://cphouseset.wordpress.com/liscense-and-terms-of-use/.                 #
  14. #                                                                              #
  15. #      Contact:                                                                #
  16. #  NeonBlack - neonblack23@live.com (e-mail) or "neonblack23" on skype         #
  17. ###--------------------------------------------------------------------------###
  18.  
  19. ###--------------------------------------------------------------------------###
  20. #  V1.0 - 9.6.2012~9.8.2012                                                    #
  21. #   Wrote and debugged main script                                             #
  22. ###--------------------------------------------------------------------------###
  23.  
  24. ###--------------------------------------------------------------------------###
  25. #      Compatibility:                                                          #
  26. #  Alias:      - Game_Map: setup                                               #
  27. #                Scene_Map: perform_transfer                                   #
  28. #  Overwrites  - Game_Event: conditions_met?, update                           #
  29. #  New Methods - Game_System: dungeon?, dungeon, create_dungeon                #
  30. #                Game_Map: random_map?, setup_random_map, choose_random,       #
  31. #                          get_block, get_random_index, convert_direction,     #
  32. #                          setup_first_events, check_for_events,               #
  33. #                          create_treasure, add_new_event,                     #
  34. #                          reset_all_self_switches                             #
  35. #                Game_Player: set_start_pos                                    #
  36. #                Game_Interpreter: create_dungeon                              #
  37. #                Game_Dungeon: initialize, new_dungeon, create_alts,           #
  38. #                              add_path, get_direction, check_dir, invert,     #
  39. #                              add_treasure, map_error, map, start_pos,        #
  40. #                              end_pos                                         #
  41. ###--------------------------------------------------------------------------###
  42.  
  43. ###--------------------------------------------------------------------------###
  44. #      Instructions:                                                           #
  45. #  Place this script in the "Materials" section of the scripts above "Main".   #
  46. #  This script has only a single setting but it still has quite a few          #
  47. #  conditions for it to work properly.  Be sure to consult the user manual to  #
  48. #  ensure everything is set up properly for it to work.  The tags used by the  #
  49. #  system are defined below:                                                   #
  50. #                                                                              #
  51. #    <random> - This must be placed in a map's notebox for it to be a random   #
  52. #               dungeon.  Without it, nothing will happen to the map.          #
  53. #    <super> - Put this in an event's name on a random map and the event will  #
  54. #              always appear in the upper left hand corner of the map.  This   #
  55. #              is useful for parallel events that need to be running only in   #
  56. #              the map, such as certain bits of dialogue.                      #
  57. #    <treasure> - Put this in an event's name and it will appear on tiles set  #
  58. #                 to region id 3 in "treasure rooms" on the map.  Only the     #
  59. #                 one with the lowest ID will show up.                         #
  60. #                                                                              #
  61. #  To go to a random dungeon, use the following script call and then simply    #
  62. #  transfer to any random dungeon map.                                         #
  63. #                                                                              #
  64. #    create_dungeon(length, alts, width, height)                               #
  65. #                                                                              #
  66. #      length - This is the length of the dungeon's main path.  This includes  #
  67. #               the very end room.  Set this to a number to have that many     #
  68. #               rooms between the start and end room.                          #
  69. #      alts - The number of branching alternate paths to have.  These paths    #
  70. #             can start from any existing room on the map and can be any       #
  71. #             length between 1 and room and "lenth" rooms.                     #
  72. #      width - The width of the total space allotted for rooms.  By default,   #
  73. #              empty "rooms" are placed on all edges of the map.  Setting      #
  74. #              this to a high number will give the generator more horizontal   #
  75. #              room to build in.                                               #
  76. #      height - Pretty much the same as "width" but with vertical room.        #
  77. #                                                                              #
  78. #      Maps:                                                                   #
  79. #  Maps must be created with a total of 40 "rooms" that the dungeon selects    #
  80. #  to draw from.  This creates a set of rooms that is 8 wide and 5 tall.       #
  81. #  Every room must be the same size, but the size of the rooms and the size    #
  82. #  of the set is pretty much infinite.  The first 16 rooms are the standard    #
  83. #  rooms that will show up based on the random value you set in the config     #
  84. #  section.  The open paths, in order, are as follows:                         #
  85. #   (Note: U = Up, R = Right, D = Down, L = Left)                              #
  86. #                                                                              #
  87. #     [none] [  U ] [  R ] [ UR ] [  D ] [ UD ] [ DR ] [ URD]                  #
  88. #     [  L ] [ UL ] [ LR ] [ LUR] [ LD ] [ UDL] [ LRD] [URDL]                  #
  89. #                                                                              #
  90. #  The next 16 tiles follow the same pattern.  These are the "uncommon" tiles  #
  91. #  that only appear with a certain chance based on the random value set        #
  92. #  below.                                                                      #
  93. #                                                                              #
  94. #  The final 8 tiles are the start and end points.  The first 4, your start    #
  95. #  points follow the pattern up, right, down, left.  The last 4 are the end    #
  96. #  points and follow the same pattern.                                         #
  97. #                                                                              #
  98. #  The end result map should be something like this crude ascii                #
  99. #  representation:                                                             #
  100. #                                                                              #
  101. #       ╨ ╞ ╚ ╥ ║ ╔ ╠                                                          #
  102. #     ╡ ╝ ═ ╩ ╗ ╣ ╦ ╬                                                          #
  103. #       ╨ ╞ ╚ ╥ ║ ╔ ╠                                                          #
  104. #     ╡ ╝ ═ ╩ ╗ ╣ ╦ ╬                                                          #
  105. #     ╨ ╞ ╥ ╡ ╨ ╞ ╥ ╡                                                          #
  106. #                                                                              #
  107. #      Regions:                                                                #
  108. #  Regions are very important for telling the map where certain things should  #
  109. #  happen.  The region tags that are reserved by the generator are             #
  110. #  1, 2, 3, 4, 6, and 8.                                                       #
  111. #  Region 3 is used by the generator to determine where to put treasures.      #
  112. #  When a room is designated as a treasure room (the end of an alt path) it    #
  113. #  will search the room for a region 3 and place treasure chests on ANY found  #
  114. #  (see above for how to mark these).                                          #
  115. #  Regions 1, 2, 4, 6, and 8 are used for the start points.  As it is          #
  116. #  creating the dungeon, it will take the last of these regions that it        #
  117. #  copies and set it as the start point.  As a general rule, only put one of   #
  118. #  any of these in each of the 4 start rooms.  When using region 1, the        #
  119. #  player's facing direction is determined by the transfer event.  If you      #
  120. #  want the direction to change based on which room the player ends up in,     #
  121. #  user 2, 4, 6, or 8.  These will set the player to face the same direction   #
  122. #  as the same number on the numpad, for example, region 8 will place the      #
  123. #  player facing up.                                                           #
  124. #                                                                              #
  125. #      Events:                                                                 #
  126. #  All other events work normally.  When a room is copied over to the newly    #
  127. #  created map, all events are taken with it and placed in exactly the same    #
  128. #  spot.  The only exceptions are events marked as treasures and events        #
  129. #  marked as super.                                                            #
  130. ###--------------------------------------------------------------------------###
  131.  
  132. ###--------------------------------------------------------------------------###
  133. #      Config:                                                                 #
  134. module CP              # Do not edit                                           #
  135. module RANDOM_DUNGEON  #  these two lines.                                     #
  136. #                                                                              #
  137. # This number determines the odds of the secondary room/tile appearing in a    #
  138. # dungeon.  The higher the number, the less the odds.                          #
  139. RANDOM = 5 # Default = 5                                                       #
  140. ###--------------------------------------------------------------------------###
  141.  
  142.  
  143. end
  144. end
  145.  
  146. module Console  ## Adds the console command for creating a map.
  147.   class << self  ## Alias the addition method with a rescue escape.
  148.     alias cp_rdg_new_console_commands new_console_commands rescue nil
  149.   end
  150.  
  151.   def self.new_console_commands
  152.     cp_rdg_new_console_commands  ## Adds the new command.
  153.     new_command("map", :cp_rdg_draw_map_do, :cp_rdg_draw_map_help)
  154.   end
  155.  
  156.   def self.cp_rdg_draw_map_help  ## The help text to be displayed.
  157.     push_string("Draws the randomly generated map if set.")
  158.   end
  159.  
  160.   def self.cp_rdg_draw_map_do(*args)
  161.     dungeon = $game_system.dungeon  ## Checks if a map is created.
  162.     return push_string("No dungeon drawn.") if dungeon.nil? || dungeon.map.nil?
  163.     dh = dungeon.height + 2  ## Adds the outer edge to the map.
  164.     dw = dungeon.width + 2
  165.     dh.times do |i1|
  166.       temp = []  ## Gets a row for the console.
  167.       dw.times do |i2|  
  168.         temp.push dungeon.map[i2, i1]
  169.       end
  170.       string = ""
  171.       chars = "░╨╞╚╥║╔╠╡╝═╩╗╣╦╬"
  172.       temp.each {|n| string += chars[n]}  ## Draws the map in ascii.
  173.       push_string(string)  ## Displays a line of the map.
  174.     end
  175.   end
  176. end
  177.  
  178. class Game_System
  179.   def dungeon?  ## Check if a dungeon exists.
  180.     return dungeon ? true : false
  181.   end
  182.  
  183.   def dungeon  ## Returns either the dungeon or nil.
  184.     return nil if @dungeon.nil? || @dungeon.map.nil?
  185.     return @dungeon
  186.   end
  187.  
  188.   def create_dungeon(length = nil, alts = 0, width = 100, height = 100)
  189.     @dungeon = Game_Dungeon.new(length, alts, width, height)
  190.   end  ## Stores a created dungeon.
  191. end
  192.  
  193. class Game_Map
  194.   attr_accessor :map_id
  195.  
  196.   alias cp_rdg_setup setup unless $@
  197.   def setup(*args)  ## Aliased to create the random dungeon.
  198.     cp_rdg_setup(*args)
  199.     return unless random_map?  ## Return the old dungeon if not random.
  200.     return if $game_system.dungeon.nil?
  201.     old_data = @map.data.dup  ## Holds onto the old stuff.
  202.     old_events = @map.events.dup
  203.     width = @map.width  ## New dungeon is prepared.
  204.     height = @map.height
  205.     rwidth = width / 8
  206.     rheight = height / 5
  207.     @map.width = ($game_system.dungeon.width + 2) * rwidth
  208.     @map.height = ($game_system.dungeon.height + 2) * rheight
  209.     @map.data = Table.new(@map.width, @map.height, 4)
  210.     @events = {}
  211.     setup_first_events  ## Sets up the super events.
  212.     setup_random_map(rwidth, rheight, old_data)  ## Creates the dungeon.
  213.     reset_all_self_switches  ## Resets all self switches in the dungeon.
  214.   end
  215.  
  216.   def random_map?  ## Checks if the map is a random map.
  217.     return @map.note.include?("<random>")
  218.   end
  219.  
  220.   def setup_random_map(rwidth, rheight, old_data)
  221.     mapw = $game_system.dungeon.width + 2
  222.     maph = $game_system.dungeon.height + 2
  223.     mapw.times do |x|
  224.       maph.times do |y|
  225.         choose_random  ## Chooses the room to mimic (standard/special)
  226.         rheight.times do |i1|
  227.           rwidth.times do |i2|
  228.             ndx = get_random_index(x, y)  ## Gets the room index.
  229.             blockx = x * rwidth
  230.             blocky = y * rheight  ## Checks and creates events.
  231.             check_for_events(ndx, rwidth, rheight, i1, i2, blockx, blocky)
  232.             4.times do |i3|  ## Creates the room.
  233.               square = get_block(ndx, rwidth, rheight, i1, i2, i3, old_data)
  234.               @map.data[blockx + i1, blocky + i2, i3] = square
  235.               next unless i3 == 3
  236.               region = square >> 8
  237.               create_treasure(blockx + i1, blocky + i2, x, y) if region == 3
  238.               next unless [32, 33, 34, 35].include?(ndx)
  239.               next unless [1, 2, 4, 6, 8].include?(region)
  240.               region = 0 if region == 1  ## Sets player start position.
  241.               $game_player.set_start_pos(blockx + i1, blocky + i2, region)
  242.             end
  243.           end
  244.         end
  245.       end
  246.     end
  247.   end
  248.  
  249.   def choose_random  ## Sets the type of room (standard/special).
  250.     rnum = rand(CP::RANDOM_DUNGEON::RANDOM)
  251.     @random_tile = rnum == 0 ? true : false
  252.   end
  253.  
  254.   def get_block(index, rwidth, rheight, x, y, layer, old_data)
  255.     ndx = (@random_tile && index < 16) ? index + 16 : index
  256.     ix = (ndx % 8) * rwidth + x  ## Gets a single tile from a room.
  257.     iy = (ndx / 8) * rheight + y
  258.     return old_data[ix, iy, layer]
  259.   end
  260.  
  261.   def get_random_index(x, y)  ## Gets the index of the room.
  262.     direction = $game_system.dungeon.map[x, y]
  263.     index = direction  ## Start and end point index modifs.
  264.     if [x - 1, y - 1] == $game_system.dungeon.start_pos
  265.       index = convert_direction(direction, true)
  266.     elsif [x - 1, y - 1] == $game_system.dungeon.end_pos
  267.       index = convert_direction(direction, false)
  268.     end
  269.     return index
  270.   end
  271.  
  272.   def convert_direction(dir, start)
  273.     case dir  ## Converts the index for start and end points.
  274.     when 1
  275.       return start ? 32 : 36
  276.     when 2
  277.       return start ? 33 : 37
  278.     when 4
  279.       return start ? 34 : 38
  280.     when 8
  281.       return start ? 35 : 39
  282.     end
  283.   end
  284.  
  285.   def setup_first_events  ## Prepares the super events.
  286.     @map.events.each do |i, event|
  287.       next unless event.name.include?("<super>")
  288.       add_new_event(event, 0, 0)
  289.     end
  290.   end
  291.  
  292.   def check_for_events(index, rwidth, rheight, x, y, placex, placey)
  293.     ndx = (@random_tile && index < 16) ? index + 16 : index
  294.     ix = (ndx % 8) * rwidth + x  ## Gets all events except tagged events.
  295.     iy = (ndx / 8) * rheight + y
  296.     px = placex + x
  297.     py = placey + y
  298.     @map.events.each do |i, event|
  299.       next if event.name.include?("<treasure>") || event.name.include?("<super>")
  300.       next unless event.x == ix && event.y == iy
  301.       add_new_event(event, px, py)
  302.       return
  303.     end
  304.   end
  305.  
  306.   def create_treasure(x, y, mx, my)  ## Sets up treasure events.
  307.     return unless $game_system.dungeon.treasure.include?([mx - 1, my - 1])
  308.     @map.events.each do |i, event|
  309.       next unless event.name.include?("<treasure>")
  310.       add_new_event(event, x, y)
  311.       return
  312.     end
  313.   end
  314.  
  315.   def add_new_event(event, x, y)
  316.     id = @events.size + 1  ## Places a new event on the map.
  317.     @events[id] = Game_Event.new(@map_id, event)
  318.     @events[id].moveto(x, y)
  319.     @events[id].id = id
  320.   end
  321.  
  322.   def reset_all_self_switches  ## Resets the self switches of all events.
  323.     @events.each do |i, event|
  324.       ["A", "B", "C", "D"].each do |switch|
  325.         key = [@map_id, event.id, switch]
  326.         $game_self_switches[key] = false
  327.       end
  328.     end
  329.   end
  330. end
  331.  
  332. class Game_CharacterBase
  333.   attr_accessor :id  ## Allows the "@id" variable to be changed for above.
  334. end
  335.  
  336. class Game_Event < Game_Character  ## Two changes required for events to work.
  337.   def conditions_met?(page)
  338.     c = page.condition
  339.     if c.switch1_valid
  340.       return false unless $game_switches[c.switch1_id]
  341.     end
  342.     if c.switch2_valid
  343.       return false unless $game_switches[c.switch2_id]
  344.     end
  345.     if c.variable_valid
  346.       return false if $game_variables[c.variable_id] < c.variable_value
  347.     end
  348.     if c.self_switch_valid
  349.       key = [@map_id, @id, c.self_switch_ch]  ## Only modded this line....
  350.       return false if $game_self_switches[key] != true
  351.     end
  352.     if c.item_valid
  353.       item = $data_items[c.item_id]
  354.       return false unless $game_party.has_item?(item)
  355.     end
  356.     if c.actor_valid
  357.       actor = $game_actors[c.actor_id]
  358.       return false unless $game_party.members.include?(actor)
  359.     end
  360.     return true
  361.   end
  362.  
  363.   def update
  364.     super
  365.     check_event_trigger_auto
  366.     return unless @interpreter  ## Also modded the line below for @id.
  367.     @interpreter.setup(@list, @id) unless @interpreter.running?
  368.     @interpreter.update
  369.   end
  370. end
  371.  
  372. class Game_Player < Game_Character
  373.   attr_reader :new_map_id
  374.  
  375.   def set_start_pos(x, y, dir)  ## Allows a start position to be set.
  376.     @new_x = x                  ## Required to place the player on a random map.
  377.     @new_y = y
  378.     set_direction(dir)
  379.   end
  380. end
  381.  
  382. class Game_Interpreter  ## Allows the new dungeon to be created.
  383.   def create_dungeon(length = nil, alts = 0, width = 100, height = 100)
  384.     $game_system.create_dungeon(length, alts, width, height)
  385.   end
  386. end
  387.  
  388. class Game_Dungeon
  389.   attr_reader :width
  390.   attr_reader :height
  391.   attr_reader :treasure
  392.  
  393.   def initialize(length = nil, alts = 0, width = 100, height = 100)
  394.     @width = [width, 1].max  ## Sets the default values of the dungeon.
  395.     @height = [height, 1].max
  396.     @alts = [alts, 0].max
  397.     @length = length
  398.     @iterations = 0  ## Resets iteration.
  399.     new_dungeon unless length.nil?  ## Creates a dungeon.
  400.   end
  401.  
  402.   def new_dungeon(length = @length)  ## Creates a dungeon of a certain length.
  403.     @map = Table.new(@width + 2, @height + 2)
  404.     @done = false
  405.     @treasure = []
  406.     @startx = rand(@width)
  407.     @starty = rand(@height)
  408.     posx = @startx
  409.     posy = @starty
  410.     length.times do
  411.       dir = get_direction(posx, posy)
  412.       if dir
  413.         posx, posy = add_path(posx, posy, dir)
  414.       else  ## Performs error handling.
  415.         @iterations += 1
  416.         return map_error if @iterations >= 50
  417.         new_dungeon(length)
  418.         break
  419.       end
  420.     end
  421.     return if @done  ## Prevents section from being performed for each error.
  422.     @endx = posx
  423.     @endy = posy
  424.     create_alts(length + 2, width * height) if @map
  425.     @iterations = 0
  426.     @done = true
  427.   end
  428.  
  429.   def create_alts(length, area)  ## Creates several possible alt paths.
  430.     return if @alts == 0
  431.     @alts.times do
  432.       return if length >= area
  433.       x = rand(width)
  434.       y = rand(height)
  435.       redo unless @map[x + 1, y + 1] != 0
  436.       redo if x == @startx && y == @starty
  437.       redo if x == @endx && y == @endy
  438.       dir = get_direction(x, y)
  439.       redo if dir.nil?
  440.       x, y = add_path(x, y, dir)
  441.       length += 1
  442.       rand(@length).times do
  443.         dir = get_direction(x, y)
  444.         break if dir.nil?
  445.         x, y = add_path(x, y, dir)
  446.         length += 1
  447.       end
  448.       add_treasure(x, y)  ## Creates treasure rooms at each section.
  449.     end
  450.   end
  451.  
  452.   def add_path(x, y, dir)  ## Adds an opening to the current and next blocks.
  453.     @map[x + 1, y + 1] += dir
  454.     x += 1 if dir == 2
  455.     x -= 1 if dir == 8
  456.     y += 1 if dir == 4
  457.     y -= 1 if dir == 1
  458.     @map[x + 1, y + 1] += invert(dir)
  459.     return x, y
  460.   end
  461.  
  462.   def get_direction(x, y)  ## Gets all possible directions to move.
  463.     movements = [1, 2, 4, 8]
  464.     movements -= check_dir(x, y)
  465.     return nil if movements.nil?
  466.     dir = rand(movements.size)  ## Returns nil or a random direction.
  467.     return movements[dir]
  468.   end
  469.  
  470.   def check_dir(x, y)  ## Shows which directions cannot be moved to.
  471.     remove = []
  472.     remove.push(1) if y == 0 || @map[x + 1, y] != 0
  473.     remove.push(2) if x == @width - 1 || @map[x + 2, y + 1] != 0
  474.     remove.push(4) if y == @height - 1 || @map[x + 1, y + 2] != 0
  475.     remove.push(8) if x == 0 || @map[x, y + 1] != 0
  476.     return remove
  477.   end
  478.  
  479.   def invert(dir)  ## Inverts a direction.
  480.     case dir
  481.     when 1
  482.       return 4
  483.     when 2
  484.       return 8
  485.     when 4
  486.       return 1
  487.     when 8
  488.       return 2
  489.     else
  490.       return 0
  491.     end
  492.   end
  493.  
  494.   def add_treasure(x, y)  ## Adds a treasure room.
  495.     @treasure.push([x, y])
  496.   end
  497.  
  498.   def map_error  ## Destroys the map on max iterations.
  499.     @map = nil
  500.     msgbox "Max iterations reached"
  501.     @iterations = 0
  502.   end
  503.  
  504.   def map  ## Shows other events a valid map if possible.
  505.     return nil unless @map
  506.     return @map
  507.   end
  508.  
  509.   def start_pos  ## Shows the start location.
  510.     return [@startx, @starty]
  511.   end
  512.  
  513.   def end_pos  ## Shows the end location.
  514.     return [@endx, @endy]
  515.   end
  516. end
  517.  
  518. class Scene_Map < Scene_Base
  519.   alias cp_rdg_perform_transfer perform_transfer unless $@
  520.   def perform_transfer  ## Redraws the map on same random map transfers.
  521.     if $game_player.new_map_id == $game_map.map_id && $game_map.random_map?
  522.       $game_map.map_id = -1
  523.     end
  524.     cp_rdg_perform_transfer
  525.   end
  526. end
  527.  
  528.  
  529. ###--------------------------------------------------------------------------###
  530. #  End of script.                                                              #
  531. ###--------------------------------------------------------------------------###
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement