Page 2 of 3

Re: How to disable automap ina floor? (trying to make labyri

Posted: Wed Jan 16, 2013 6:54 am
by Balthasar
Build the labyrinth out of secret doors in a giant open room, then place scores of open doors on every possible spot. If they open the map they won't get any information because it will just appear as a ton of doors.

I suggest using a script to spawn the doors. Much cleaner than making them all manually.

You would need to put a ton of pillars in as well to cover the gaps between the secret doors.

Re: How to disable automap ina floor? (trying to make labyri

Posted: Wed Jan 16, 2013 7:15 am
by msyblade
that is a really cool workaround! Good work! Maybe one of you scripting ninja's could get us a nifty customizable labyrinth spawning code to put in the script repository :)

Re: How to disable automap ina floor? (trying to make labyri

Posted: Wed Jan 16, 2013 2:28 pm
by vidarfreyr
But wont the secret doors just appear as walls in the map view? Perhaps use standard doors and change the model to a secret door? That way it appears as a regular door on the map.

Re: How to disable automap ina floor? (trying to make labyri

Posted: Thu Jan 17, 2013 3:57 am
by Balthasar
Yes, the secret doors appear as walls. That's why I suggested spawning regular (but open) doors over top of them to obscure the map.

-------------------------
EDIT - Actually I just tried it and it doesn't work anyway. The regular doors don't show up on the map when they are placed on top of secret doors.

Here's how to really do it:

Clone the secret door object, but set secretDoor = false.
Then cover every square in the grid with said door.
Then open up various doors to make a path through.

In the map view, it just looks like a giant clusterf*ck of doors.

Ugly video to show:

http://www.youtube.com/watch?v=VvcwdaI4 ... e=youtu.be

Re: How to disable automap ina floor? (trying to make labyri

Posted: Thu Jan 17, 2013 4:11 am
by Komag
Aha, never thought about putting doors on top like that, good idea. would have to use some door without a frame or invisible or something

Re: How to disable automap ina floor? (trying to make labyri

Posted: Thu Jan 17, 2013 4:19 am
by Balthasar
Sorry for the downright horrible quality on the video, but hopefully you get the idea.

Keep in mind this method is a major pita to implement in the editor.

Re: How to disable automap ina floor? (trying to make labyri

Posted: Thu Jan 17, 2013 4:49 am
by vidarfreyr
Very good Balthasar. I might do a labyrinth now, as previously I found them to be not very challenging. This way you can't really know which tiles you already traveled and which you only saw, making it more difficult to use the map to navigate.

Re: How to disable automap ina floor? (trying to make labyri

Posted: Thu Jan 17, 2013 6:26 pm
by HaunterV
if teh new GUI options dont work.

we need to make sure to remember we want to disable/customize the automap.

Re: How to disable automap ina floor? (trying to make labyri

Posted: Sat Jan 19, 2013 5:59 am
by dasarby
Ok, I took a stab at this.

Paste the attached at the end into a script entity called Mazebuilder.

Basic maze creation
The steps are pretty much 1) create maze instance, 2) initialize maze dimensions/location, 3) set the map layout.
Here is an example of a 4x3 maze, on level 1, at (5, 5):

Code: Select all

maze = MazeBuilder:Create()
MazeBuilder.Initalize(maze, 1, 5, 5, 4, 3)
MazeBuilder.SetMap(maze, [[
+-+-+-+-+
| |     |
+ +-+ + +
|     | |
+-+ +-+ +
|   |   |
+-+-+-+-+
]])
Keep that maze variable around! You can call MazeBuilder.SetMap over and over, with different maps, and the doors will automatically change to match the new map. If you specify a truthy third parameter, like: MazeBuilder.SetMap(maze, map, true), the doors will animate into their new state. otherwise it will happen without animation.

By default, it uses dungeon secret doors/pillars. You can change this after creation but before intialization:

Code: Select all

maze = MazeBuilder:Create()
maze.doorTypes = {"temple_secret_door"}
maze.pillarTypes = {"temple_piller}
maze.doorTypes and maze.pillarTypes are tables, so you can throw multiple values in there. if there is more than one value, the MazeBuilder will pick one at random. This way, we can add some random flavor to the maze.

Speaking of random flavor, why do we need to even set a map?

Code: Select all

maze = MazeBuilder:Create()
MazeBuilder.Initialize(maze, 1, 5, 5, 12, 12)
MazeBuilder.RandomizeMap(maze, true/false)
Here, the MazeBuilder will take care of making a random maze for you. It will fill up the entire area provided (see http://en.wikipedia.org/wiki/Maze_gener ... rst_search)

The call to RandomizeMap will return the generated map string, so you can hang on to it an re-create the same maze again at a later time. You could even put it in a scroll text or something.

Code: Select all

-- Maze Builder
-- Creates mazes out of secret doors and pillars.
--
-- Usage:
--  1. Copy this entire script into a Script entity anywhere in your dungeon.  Name it MazeBuilder
--  2. Create another script, here is where you will configure your maze
--  3. Create a new instance of the the MazeBuilder:
--	  maze = MazeBuilder:Create()
--  4. Carve out the area for your maze in the dungeon level.  Initialize the maze.  Pass the
--     dungeon level, the x,y of the upper left of your maze, and the width/height of your maze.
--        MazeBuilder.Initialize(maze, 1, 5, 5, 10, 10) -- 10x10 maze located at (5,5)
--  5. By default, all doors will be open.  Set the map to a preset one or a random one:
--        MazeBuilder.SetMap(maze, animate)
--        MazeBuilder.RandomizeMap(maze, animate)
--     mapString is a string made with +, -, and | describing the walls.  See example below.
--     Each of the functions above have an optional animate parameter, if set to true the walls
--     will animate into their new positions.
--
-- Here is an example of a 4x3 maze string:
-- +-+-+-+-+
-- | |     |
-- + +-+ + +
-- |     | |
-- +-+ +-+ +
-- |   |   |
-- +-+-+-+-+

function Create()
	local maze = {}
	maze.map = ""
	maze.doorTypes = {"dungeon_secret_door"}
	maze.pillarTypes = {"dungeon_pillar"}
	maze.eastDoors = {}
	maze.southDoors = {}
	maze.initialized = false

	return maze
end

-- Preps a map by generating doors and pillars.
--   level : the level the maze is on
--   x : the x coordinate of the upper left cell in the maze
--	 y : the y coordinate of the upper left cell in the maze\
--   w : the width of the maze, in cells
--   h : the height of the maze, in cells 
function Initialize(maze, level, x, y, w, h)
	if not (level and x and y and w and h) then
		print("Please specify level, x, y, w, and h when calling Initialize")
		return
	end
	maze.width = w
	maze.height = h
	-- vertical doors
	for i=0, maze.height - 1, 1 do
		maze.eastDoors[i] = {}
		for j=0, maze.width - 2, 1 do
			local doorType = maze.doorTypes[math.random(table.getn(maze.doorTypes))]
			local door = spawn(doorType, level, x + j, y + i, 1)
			door:setDoorState("open")
			maze.eastDoors[i][j] = door.id
		end
	end

	-- horizontal doors
	for i=0, maze.height - 2, 1 do
		maze.southDoors[i] = {}
		for j=0, maze.width - 1, 1 do
			local doorType = maze.doorTypes[math.random(table.getn(maze.doorTypes))]
			local door = spawn(doorType, level, x + j, y + i, 2)
			door:setDoorState("open")
			maze.southDoors[i][j] = door.id
		end
	end

	-- spawn pillars
	for i=1, maze.width - 1, 1 do
		for j=1, maze.height - 1, 1 do
			local pillarType = maze.pillarTypes[math.random(table.getn(maze.pillarTypes))]
			spawn(pillarType, level, x + i, y + j, 0)
		end
	end
	maze.initialized = true
end

	
-- Creates a random maze and sets the doors to that maze.
-- If animate is true, the doors will animate into their new state.
-- this function will return a string representing the map.
function RandomizeMap(maze, animate)
	if not maze.initialized then
		print("Cannot randomize maze until it has been initialized")
		return
	end
	maze.map = randomMap(maze.width, maze.height)
	processMap(maze, animate)
	return map
end

-- Sets the state of the doors based on a string representing a maze.
-- If animate is set to true, then the doors wil animate into their new state.
function SetMap(maze, map, animate)
	if not maze.initialized then
		print("Cannot set the map for the maze until it has been initialized")
		return
	end
	maze.map = map
	processMap(maze, animate)
end



--- Helper functions and the like.  Don't call these directly

-- Take a map string and sync the doors to match the map.
function processMap(maze, animate)
	local i = 0
	for line in maze.map:gmatch("[^\r\n]+") do
		i = i + 1
		if i == 1 or i == 2 * maze.height + 1 then
			-- dont do anything for the first or last line in the map
		elseif i % 2 == 0 then
			-- for even lines, we care about the vertical doors
			for j = 0, maze.width - 2, 1 do
				local door = findEntity(maze.eastDoors[i / 2 - 1][j])
				if line:sub(2 * j + 3, 2 * j + 3) == " " then
					if animate then
						door:open()
					else
						door:setDoorState("open")
					end
				else
					if animate then
						door:close()
					else
						door:setDoorState("closed")
					end
				end
			end
		else
			-- for odd lines, we do horizontal (south) doors
			for j = 0, maze.width - 1, 1 do
				local door = findEntity(maze.southDoors[(i - 1) / 2 - 1][j])
				if line:sub(2*j+2, 2*j+2) == " " then
					if animate then
						door:open()
					else
						door:setDoorState("open")
					end
				else
					if animate then
						door:close()
					else
						door:setDoorState("closed")
					end 
				end
			end
		end
	end
end


-- A Simple stack implementation.
-- Gratefully stolen from http://lua-users.org/wiki/SimpleStack

Stack = {}

-- Create a Table with stack functions
function Stack:Create()

  -- stack table
  local t = {}
  -- entry table
  t._et = {}

  -- push a value on to the stack
  function t:push(...)
    if ... then
      local targs = {...}
      -- add values
      for _,v in pairs(targs) do
        table.insert(self._et, v)
      end
    end
  end

  -- pop a value from the stack
  function t:pop(num)

    -- get num values from stack
    local num = num or 1

    -- return table
    local entries = {}

    -- get values into entries
    for i = 1, num do
      -- get last entry
      if #self._et ~= 0 then
        table.insert(entries, self._et[#self._et])
        -- remove last value
        table.remove(self._et)
      else
        break
      end
    end
    -- return unpacked entries
    return unpack(entries)
  end

  -- get entries
  function t:getn()
    return #self._et
  end

  -- list values
  function t:list()
    for i,v in pairs(self._et) do
      print(i, v)
    end
  end
  return t
end


-- given a width and height, generate a random maze.  This uses a simplified DFS
-- as outlined here: http://en.wikipedia.org/wiki/Maze_generation_algorithm#Recursive_backtracker
function randomMap(width, height)
	local visited = {}
	local map = {}
	table.insert(map, string.rep("+-", width) .. "+")
	for i=1, height, 1 do
		visited[i] = {}
		table.insert(map, string.rep("| ", width) .. "|")
		table.insert(map, string.rep("+-", width) .. "+")
	end

	current = {x=math.random(width), y=math.random(height)}
	visited[current.y][current.x] = true

	local stack = Stack:Create()

	iterations = 0

	while hasUnvisited(visited, width, height) do
		iterations = iterations + 1
		randomNeighbor = randomUnvisited(current, visited, width, height)
		if randomNeighbor ~= nil then
			stack:push(current)
			if current.x ~= randomNeighbor.x then
				openDoor(map, math.min(current.x, randomNeighbor.x), current.y, true)
			else
				openDoor(map, current.x, math.min(current.y, randomNeighbor.y), false)
			end
			current = randomNeighbor
			visited[randomNeighbor.y][randomNeighbor.x] = true
		elseif stack:getn() > 0 then
			current = stack:pop(1)
		else
			current = {x=math.random(width), y=math.random(height)}
			visited[current.y][current.x] = true
		end
	end

	s = ""
	for _, i in ipairs(map) do
		s = s .. i .. "\n"
	end
	return s
end

-- given a map as a string, the x and y coordinates of a cell, will open
-- an east or south door on that cell in the map string.
function openDoor(map, x, y, east)
	if east then
		local line = map[y * 2]
		map[y * 2] = line:sub(1, x * 2) .. " " .. line:sub(x * 2 + 2)
	else
		local line = map[y * 2 + 1]
		map[y * 2 + 1] = line:sub(1, x * 2 - 1) .. " " .. line:sub(x * 2 + 1)
	end
end

-- Given a hash of visited cells, returns a random unvisited cell adjacent
-- to a given point.  If there are no adjacent unvisited cells, return nil.
function randomUnvisited(point, visited, width, height)
	local unvisited = {}
	-- north
	if point.y - 1 >= 1 and not visited[point.y - 1][point.x] then
		table.insert(unvisited, {x=point.x, y=point.y - 1})
	end
	-- east
	if point.x + 1 <= width and not visited[point.y][point.x + 1] then
		table.insert(unvisited, {x=point.x + 1, y=point.y})
	end
	-- south
	if point.y + 1 <= height and not visited[point.y + 1][point.x] then
		table.insert(unvisited, {x=point.x, y=point.y + 1})
	end
	-- west
	if point.x - 1 >= 1 and not visited[point.y][point.x - 1] then
		table.insert(unvisited, {x=point.x - 1, y=point.y})
	end

	if table.getn(unvisited) == 0 then
		return nil
	end

	local i = math.random(table.getn(unvisited))
	return unvisited[i]
end

-- Returns if there is an unvisited node in the map
function hasUnvisited(visited, width, height)
	for i=1, height, 1 do
		for j=1, width, 1 do
			if not visited[i][j] then
				return true
			end
		end
	end
	return false
end
https://github.com/adharris/grimrock/bl ... s/maze.lua

Lemme know how it works for you all!

Re: How to disable automap ina floor? (trying to make labyri

Posted: Sat Jan 19, 2013 6:03 am
by Balthasar
Nice. Implementing such mazes manually in the editor would be a crazy headache. With the power of code, now it can be done in a practical manner. The power of code compels you!