| 
				   | 
				
| Line 1: | 
Line 1: | 
|   | [[Levelgen_example:_Funcorners|Funcorners]]  |   | [[Levelgen_example:_Funcorners|Funcorners]]  | 
| − |    | + | [[Levelgen_example:_Mazeracer|Mazeracer]]  | 
| − | To use the following script, copy it into a file called "mazegen.lua" in your levels folder, and add the following line to your level file:
  | + |  | 
| − |    | + |  | 
| − | <source lang="levelcode">
  | + |  | 
| − | Script mazegen.lua
  | + |  | 
| − | </source>
  | + |  | 
| − |    | + |  | 
| − | or, to create a smaller maze:
  | + |  | 
| − |    | + |  | 
| − | <source lang="levelcode">
  | + |  | 
| − | Script mazegen.lua 5 5 0 0 1
  | + |  | 
| − | </source>
  | + |  | 
| − |    | + |  | 
| − |    | + |  | 
| − | <source lang="lua">
  | + |  | 
| − | -------------------------------------------------------------------------------
  | + |  | 
| − | --
  | + |  | 
| − | -- MazeGen script creates a random maze
  | + |  | 
| − | -- Args: gridXSize, gridYSize, ulCornerX, ulCornerY, cellsize
  | + |  | 
| − | --
  | + |  | 
| − | --    gridXSize, gridYSize --> Number of cells in the maze (defaults to 10, 10)
  | + |  | 
| − | --    ulCornerX, ulCornerY --> Coordinates of upper left corner of maze
  | + |  | 
| − | --                        (defaults to 0, 0)
  | + |  | 
| − | --    cellsize --> Size of each maze cell (defaults to 1)
  | + |  | 
| − | --
  | + |  | 
| − | -------------------------------------------------------------------------------
  | + |  | 
| − |    | + |  | 
| − |    | + |  | 
| − | function isUnvisited(cell)
  | + |  | 
| − |   return cell["N Wall"] and cell["S Wall"] and cell["E Wall"] and cell["W Wall"]
  | + |  | 
| − | end
  | + |  | 
| − |    | + |  | 
| − |    | + |  | 
| − | function getAdjacentUnvisitedCells(cell)
  | + |  | 
| − |    | + |  | 
| − |   local x = cell["x"]
  | + |  | 
| − |   local y = cell["y"]
  | + |  | 
| − |    | + |  | 
| − |   adjCells = {}
  | + |  | 
| − |   adjDirs = {}
  | + |  | 
| − |    | + |  | 
| − |   if(not cells[x][y]["N Edge"] and isUnvisited(cells[x][y - 1])) then
  | + |  | 
| − |      table.insert(adjCells, cells[x][y - 1])
  | + |  | 
| − |      table.insert(adjDirs, "N")
  | + |  | 
| − |   end
  | + |  | 
| − |    | + |  | 
| − |   if(not cells[x][y]["S Edge"] and isUnvisited(cells[x][y + 1])) then
  | + |  | 
| − |      table.insert(adjCells, cells[x][y + 1])
  | + |  | 
| − |      table.insert(adjDirs, "S")
  | + |  | 
| − |   end
  | + |  | 
| − |    | + |  | 
| − |   if(not cells[x][y]["E Edge"] and isUnvisited(cells[x + 1][y])) then
  | + |  | 
| − |      table.insert(adjCells, cells[x + 1][y])
  | + |  | 
| − |      table.insert(adjDirs, "E")
  | + |  | 
| − |   end
  | + |  | 
| − |    | + |  | 
| − |   if(not cells[x][y]["W Edge"] and isUnvisited(cells[x - 1][y])) then
  | + |  | 
| − |      table.insert(adjCells, cells[x - 1][y])
  | + |  | 
| − |      table.insert(adjDirs, "W")
  | + |  | 
| − |   end
  | + |  | 
| − | end
  | + |  | 
| − |    | + |  | 
| − |    | + |  | 
| − | -- Knock down wall to neighbor cell, and neighbor's wall to current cell
  | + |  | 
| − | function knockDownWallToCell(cell, dir, adjacentCell)
  | + |  | 
| − |   cell[dir .. " Wall"] = false
  | + |  | 
| − |   adjacentCell[oppDir(dir) .. " Wall"] = false
  | + |  | 
| − | end
  | + |  | 
| − |    | + |  | 
| − |    | + |  | 
| − | function oppDir(dir)
  | + |  | 
| − |   if(dir == "N") then return "S" end
  | + |  | 
| − |   if(dir == "S") then return "N" end
  | + |  | 
| − |   if(dir == "E") then return "W" end
  | + |  | 
| − |   if(dir == "W") then return "E" end
  | + |  | 
| − | end
  | + |  | 
| − |    | + |  | 
| − |    | + |  | 
| − | function initGridCells(xsize, ysize)
  | + |  | 
| − |   for x = 0, xsize + 1 do    -- Add extra cell to avoid bounds checking later
  | + |  | 
| − |      cells[x] = {}
  | + |  | 
| − |      for y = 0, ysize + 1 do
  | + |  | 
| − |         cells[x][y] = {}
  | + |  | 
| − |    | + |  | 
| − |         cells[x][y]["N Wall"] = true
  | + |  | 
| − |         cells[x][y]["S Wall"] = true
  | + |  | 
| − |         cells[x][y]["E Wall"] = true
  | + |  | 
| − |         cells[x][y]["W Wall"] = true
  | + |  | 
| − |    | + |  | 
| − |         cells[x][y]["x"] = x
  | + |  | 
| − |         cells[x][y]["y"] = y
  | + |  | 
| − |    | + |  | 
| − |         cells[x][y]["N Edge"] = (y == 1)
  | + |  | 
| − |         cells[x][y]["S Edge"] = (y == gridYSize)
  | + |  | 
| − |         cells[x][y]["E Edge"] = (x == gridXSize)
  | + |  | 
| − |         cells[x][y]["W Edge"] = (x == 1)
  | + |  | 
| − |      end
  | + |  | 
| − |   end
  | + |  | 
| − | end
  | + |  | 
| − |    | + |  | 
| − |    | + |  | 
| − | function outputMaze()
  | + |  | 
| − |    | + |  | 
| − |    -- We'll output our maze as a series of tiles, each being 2 x cellsize square
  | + |  | 
| − |    -- Divide each tile into quadrants, as follows:
  | + |  | 
| − |    --           X----+----+
  | + |  | 
| − |    --           | UL | UR |    * denotes point (cenx, ceny)
  | + |  | 
| − |    --           |    |    |    size of each cell is (gridXSize, gridXSize)
  | + |  | 
| − |    --           +----*----+    coords of point X on upper left cell are
  | + |  | 
| − |    --           | LL | LR |       (ulCornerX, ulCornerY)
  | + |  | 
| − |    --           |    |    |    UL is always filled in, LR is always open
  | + |  | 
| − |    --           +----+----+    LL is filled in when an E wall is present
  | + |  | 
| − |    --                          UR is filled in when a N wall is present
  | + |  | 
| − |    --  When a series of these tiles are tiled together, a maze will be drawn
  | + |  | 
| − |    --  with an open bottom and open right side, which will be closed at the end.
  | + |  | 
| − |    --
  | + |  | 
| − |    --  Wall segments are drawn as 2-pt horizontal lines with the appropriate
  | + |  | 
| − |    --  thickness to make them square.  Adjacent wall segments are partially
  | + |  | 
| − |    --  merged into longer horizontal lines to reduce vertices and therefore
  | + |  | 
| − |    --  level transfer size.  Note we're taking advantage of Bitfighter's vertex
  | + |  | 
| − |    --  thinning algorithms to make our job just a bit easier.
  | + |  | 
| − |    | + |  | 
| − |    local gridsize = levelgen:getGridSize() + 1 -- Add 1 to get a tiny bit of
  | + |  | 
| − |                                                -- overlap to make walls merge
  | + |  | 
| − |    local wallsize = gridsize * cellsize
  | + |  | 
| − |    local points
  | + |  | 
| − |    | + |  | 
| − |    for y = 1, gridYSize do
  | + |  | 
| − |    | + |  | 
| − |       local xpoints = { }
  | + |  | 
| − |    | + |  | 
| − |       for x = 1, gridXSize do
  | + |  | 
| − |          local cenx = ulCornerX + (2 * x - 1) * cellsize
  | + |  | 
| − |          local ceny = ulCornerY + (2 * y - 1) * cellsize
  | + |  | 
| − |          local cell = cells[x][y]
  | + |  | 
| − |    | + |  | 
| − |    | + |  | 
| − |          -- UL quadrant is always drawn
  | + |  | 
| − |          if(#xpoints == 0) then        -- No line in progress
  | + |  | 
| − |             xpoints = { Point(cenx - cellsize, ceny - cellsize * 0.5) }
  | + |  | 
| − |          end
  | + |  | 
| − |    | + |  | 
| − |          -- UR quadrant drawn only when a N wall is present
  | + |  | 
| − |        local endx
  | + |  | 
| − |    | + |  | 
| − |        if(cell["N Wall"]) then
  | + |  | 
| − |          endx = cenx + cellsize
  | + |  | 
| − |        else
  | + |  | 
| − |          endx = cenx
  | + |  | 
| − |          end
  | + |  | 
| − |    | + |  | 
| − |    | + |  | 
| − |        if(not cell["N Wall"] or cell["E Edge"]) then
  | + |  | 
| − |              table.insert(xpoints, Point(endx, ceny - cellsize * 0.5) )
  | + |  | 
| − |    | + |  | 
| − |           levelgen:addWall(wallsize, false, xpoints)
  | + |  | 
| − |           xpoints = { }
  | + |  | 
| − |        end
  | + |  | 
| − |    | + |  | 
| − |          -- LL quadrant drawn only when a W wall is present
  | + |  | 
| − |          if(cell["W Wall"]) then
  | + |  | 
| − |             local pts = { Point(cenx - cellsize, ceny + cellsize * 0.5),
  | + |  | 
| − |                           Point(cenx            , ceny + cellsize * 0.5) }
  | + |  | 
| − |             levelgen:addWall(wallsize, false, pts)
  | + |  | 
| − |          end
  | + |  | 
| − |    | + |  | 
| − |          -- LR quadrant is always open, so do nothing
  | + |  | 
| − |       end
  | + |  | 
| − |    | + |  | 
| − |    end
  | + |  | 
| − |    | + |  | 
| − |    -- Now add walls along entire bottom and right sides of the maze
  | + |  | 
| − |    local pts = { Point(ulCornerX,
  | + |  | 
| − |                      ulCornerY + 2 * gridYSize * cellsize + cellsize * 0.5),
  | + |  | 
| − |                  Point(ulCornerX + 2 * gridXSize * cellsize + cellsize * 0.5,
  | + |  | 
| − |                        ulCornerY + 2 * gridYSize * cellsize + cellsize * 0.5),
  | + |  | 
| − |                  Point(ulCornerX + 2 * gridXSize * cellsize + cellsize * 0.5,
  | + |  | 
| − |                        ulCornerY) }
  | + |  | 
| − |    levelgen:addWall(wallsize, false, pts)
  | + |  | 
| − | end
  | + |  | 
| − |    | + |  | 
| − |    | + |  | 
| − |    | + |  | 
| − | -----------------------------------------------------------
  | + |  | 
| − | -----------------------------------------------------------
  | + |  | 
| − | -----------------------------------------------------------
  | + |  | 
| − | -----------------------------------------------------------
  | + |  | 
| − | -----------------------------------------------------------
  | + |  | 
| − | -----------------------------------------------------------
  | + |  | 
| − | -----------------------------------------------------------
  | + |  | 
| − | -----------------------------------------------------------
  | + |  | 
| − | -----------------------------------------------------------
  | + |  | 
| − |    | + |  | 
| − |    | + |  | 
| − | -- Global variables
  | + |  | 
| − |    | + |  | 
| − | -- Remember, in Bitfighter, all global variables must be
  | + |  | 
| − | -- defined in the main block!
  | + |  | 
| − |    | + |  | 
| − | gridXSize = tonumber(arg[1])
  | + |  | 
| − | gridYSize = tonumber(arg[2])
  | + |  | 
| − | ulCornerX = tonumber(arg[3])
  | + |  | 
| − | ulCornerY = tonumber(arg[4])
  | + |  | 
| − | cellsize = tonumber(arg[5])
  | + |  | 
| − |    | + |  | 
| − | if gridXSize == nil then gridXSize = 10 end
  | + |  | 
| − | if gridYSize == nil then gridYSize = 10 end
  | + |  | 
| − | if ulCornerX == nil then ulCornerX = 0 end
  | + |  | 
| − | if ulCornerY == nil then ulCornerY = 0 end
  | + |  | 
| − | if cellsize == nil then cellsize = 1 end
  | + |  | 
| − |    | + |  | 
| − |    | + |  | 
| − | cellStack = List:new()
  | + |  | 
| − |    | + |  | 
| − | cells = {}
  | + |  | 
| − | adjCells = {}
  | + |  | 
| − | adjDirs = {}
  | + |  | 
| − |    | + |  | 
| − | -- Initialize cells
  | + |  | 
| − | initGridCells(gridXSize, gridYSize)
  | + |  | 
| − |    | + |  | 
| − |    | + |  | 
| − | -- Get starting cell
  | + |  | 
| − | local x = math.random(gridXSize)
  | + |  | 
| − | local y = math.random(gridYSize)
  | + |  | 
| − |    | + |  | 
| − | local currCell = cells[x][y]
  | + |  | 
| − | local totalCells = gridXSize * gridYSize
  | + |  | 
| − |    | + |  | 
| − | local visitedCells = 1
  | + |  | 
| − |    | + |  | 
| − | while visitedCells < totalCells do
  | + |  | 
| − |    | + |  | 
| − |   -- Find all adjacent cells with all walls intact (sets adjCells and adjDirs)
  | + |  | 
| − |   getAdjacentUnvisitedCells(currCell)
  | + |  | 
| − |   if(#adjCells > 0) then                   -- More than one?
  | + |  | 
| − |      local adj = math.random(#adjCells)    -- Pick one at random
  | + |  | 
| − |           knockDownWallToCell(currCell, adjDirs[adj], adjCells[adj])
  | + |  | 
| − |    | + |  | 
| − |           -- Push current cell location on the cellStack; we'll revisit it later
  | + |  | 
| − |           cellStack:pushright(currCell)
  | + |  | 
| − |    | + |  | 
| − |           -- Make the randomly selected adjacent cell the new current cell
  | + |  | 
| − |           currCell = adjCells[adj]
  | + |  | 
| − |    | + |  | 
| − |      visitedCells = visitedCells + 1
  | + |  | 
| − |   else
  | + |  | 
| − |      -- This cell has no neighbors, so let's grab one from the stack
  | + |  | 
| − |      currCell = cellStack:popright()
  | + |  | 
| − |   end
  | + |  | 
| − | end
  | + |  | 
| − |    | + |  | 
| − | -- Output the results
  | + |  | 
| − | outputMaze()
  | + |  | 
| − |    | + |  | 
| − |    | + |  | 
| − | </source>
  | + |  |