181 lines
6.8 KiB
Lua
181 lines
6.8 KiB
Lua
-- Simplistic random dungeon generator lua script
|
|
-- You can call this from Editor for example, and it will load a complete dungeon from set pieces
|
|
|
|
local modelpath = "../models/"
|
|
|
|
dungeon={
|
|
|
|
Generate = function(complexity)
|
|
|
|
local scalingMat = matrix.Scale(Vector(6,6,6))
|
|
|
|
-- This will hold the bounding boxes of the placed segments
|
|
local boxes = {}
|
|
-- This holds unconnected exits
|
|
local exits = {}
|
|
-- If an exit should be placed, look up its transform here
|
|
local exitTransforms = {}
|
|
|
|
-- threshold so that aabbs snapped to each other are not detected as intersecting
|
|
-- because segments are needed to be snapped to each other
|
|
-- chose this value so that it also eliminates floating point errors
|
|
local aabb_threshold = Vector(0.05,0.05,0.05)
|
|
|
|
-- Shrink an AABB by a threshold vector
|
|
local function ShrinkAABBThreshold(aabb, threshold)
|
|
return AABB(aabb.GetMin():Add(threshold), aabb.GetMax():Subtract(threshold))
|
|
end
|
|
|
|
-- Check if a segment can be placed or not by specifying its axis aligned bounding box
|
|
local function CheckSegmentCanBePlaced(aabb)
|
|
aabb = ShrinkAABBThreshold(aabb, aabb_threshold)
|
|
|
|
for i,box in ipairs(boxes) do
|
|
if box.Intersects(aabb) then
|
|
return false
|
|
end
|
|
end
|
|
|
|
table.insert(boxes,aabb)
|
|
|
|
return true
|
|
end
|
|
|
|
-- Merge rooms if their exits are intersecting, but close them if not
|
|
local function MergeExits()
|
|
for i = 1, #exits do
|
|
local place = true
|
|
for j = 1, #exits do
|
|
if i~=j and exits[i].Intersects(exits[j]) then
|
|
place = false
|
|
break
|
|
end
|
|
end
|
|
|
|
if place then
|
|
LoadModel(modelpath .. "dungeon/end.wiscene",exitTransforms[i])
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Create physical dungeon segments recursively
|
|
local function GenerateDungeon(remaining, pos, rotY)
|
|
remaining = remaining - 1
|
|
local rotMat = matrix.RotationY(rotY)
|
|
local transformMat = matrix.Multiply(rotMat, matrix.Multiply(matrix.Translation(pos), scalingMat))
|
|
|
|
-- Mark exit point if the generation ended and return from recursion
|
|
if (remaining < 0) then
|
|
table.insert(exits, ShrinkAABBThreshold( AABB(Vector(-0.5,0,0),Vector(0.5,1,0.5)).Transform(transformMat), aabb_threshold) )
|
|
table.insert(exitTransforms, transformMat)
|
|
return;
|
|
end
|
|
|
|
local select = math.random(0, 100)
|
|
if(select < 80) then -- common pieces
|
|
local select2 = math.random(0, 6)
|
|
if(select2 < 1) then --left turn
|
|
if CheckSegmentCanBePlaced(AABB(Vector(-1,0,0),Vector(1,2,2)).Transform(transformMat)) then
|
|
LoadModel(modelpath .. "dungeon/turnleft.wiscene",transformMat)
|
|
GenerateDungeon(remaining,pos:Add(Vector(-1,0,1):Transform(rotMat)),rotY-0.5*math.pi)
|
|
else
|
|
GenerateDungeon(remaining,pos,rotY)
|
|
end
|
|
elseif(select2 < 2) then --right turn
|
|
if CheckSegmentCanBePlaced(AABB(Vector(-1,0,0),Vector(1,2,2)).Transform(transformMat)) then
|
|
LoadModel(modelpath .. "dungeon/turnright.wiscene",transformMat)
|
|
GenerateDungeon(remaining,pos:Add(Vector(1,0,1):Transform(rotMat)),rotY+0.5*math.pi)
|
|
else
|
|
GenerateDungeon(remaining,pos,rotY)
|
|
end
|
|
elseif(select2 < 3) then --t-junction
|
|
if CheckSegmentCanBePlaced(AABB(Vector(-1,0,0),Vector(1,2,2)).Transform(transformMat)) then
|
|
LoadModel(modelpath .. "dungeon/tjunction.wiscene",transformMat)
|
|
-- right
|
|
GenerateDungeon(remaining,pos:Add(Vector(1,0,1):Transform(rotMat)),rotY+0.5*math.pi)
|
|
-- left
|
|
GenerateDungeon(remaining,pos:Add(Vector(-1,0,1):Transform(rotMat)),rotY-0.5*math.pi)
|
|
else
|
|
GenerateDungeon(remaining,pos,rotY)
|
|
end
|
|
elseif(select2 < 4) then --cross-junction
|
|
if CheckSegmentCanBePlaced(AABB(Vector(-1,0,0),Vector(1,2,2)).Transform(transformMat)) then
|
|
LoadModel(modelpath .. "dungeon/crossjunction.wiscene",transformMat)
|
|
-- right
|
|
GenerateDungeon(remaining,pos:Add(Vector(1,0,1):Transform(rotMat)),rotY+0.5*math.pi)
|
|
-- left
|
|
GenerateDungeon(remaining,pos:Add(Vector(-1,0,1):Transform(rotMat)),rotY-0.5*math.pi)
|
|
-- straight
|
|
GenerateDungeon(remaining,pos:Add(Vector(0,0,2):Transform(rotMat)),rotY)
|
|
else
|
|
GenerateDungeon(remaining,pos,rotY)
|
|
end
|
|
else --straight block
|
|
if CheckSegmentCanBePlaced(AABB(Vector(-1,0,0),Vector(1,2,2)).Transform(transformMat)) then
|
|
LoadModel(modelpath .. "dungeon/block.wiscene",transformMat)
|
|
GenerateDungeon(remaining,pos:Add(Vector(0,0,2):Transform(rotMat)),rotY)
|
|
else
|
|
GenerateDungeon(remaining,pos,rotY)
|
|
end
|
|
end
|
|
elseif(select < 98) then -- average pieces
|
|
local select2 = math.random(0, 100)
|
|
if( select2 < 20 ) then --small room left
|
|
if CheckSegmentCanBePlaced(AABB(Vector(-5,0,0),Vector(1,2,6)).Transform(transformMat)) then
|
|
LoadModel(modelpath .. "dungeon/smallroomleft.wiscene",transformMat )
|
|
GenerateDungeon(remaining,pos:Add(Vector(-5,0,5):Transform(rotMat)),rotY-0.5*math.pi)
|
|
else
|
|
GenerateDungeon(remaining,pos,rotY)
|
|
end
|
|
elseif( select2 < 30 ) then --odd corridor
|
|
if CheckSegmentCanBePlaced(AABB(Vector(-1,0,0),Vector(3,2,8)).Transform(transformMat)) then
|
|
LoadModel(modelpath .. "dungeon/oddcorridor.wiscene",transformMat )
|
|
GenerateDungeon(remaining,pos:Add(Vector(2,0,8):Transform(rotMat)),rotY)
|
|
else
|
|
GenerateDungeon(remaining,pos,rotY)
|
|
end
|
|
elseif( select2 < 60 ) then --up corridor
|
|
if CheckSegmentCanBePlaced(AABB(Vector(-1,0,0),Vector(1,4,6)).Transform(transformMat)) then
|
|
LoadModel(modelpath .. "dungeon/upcorridor.wiscene",transformMat )
|
|
GenerateDungeon(remaining,pos:Add(Vector(0,2,6):Transform(rotMat)),rotY)
|
|
else
|
|
GenerateDungeon(remaining,pos,rotY)
|
|
end
|
|
else --corridor
|
|
if CheckSegmentCanBePlaced(AABB(Vector(-1,0,0),Vector(1,2,6)).Transform(transformMat)) then
|
|
LoadModel(modelpath .. "dungeon/corridor.wiscene",transformMat )
|
|
GenerateDungeon(remaining,pos:Add(Vector(0,0,6):Transform(rotMat)),rotY)
|
|
else
|
|
GenerateDungeon(remaining,pos,rotY)
|
|
end
|
|
end
|
|
else -- rare pieces
|
|
if CheckSegmentCanBePlaced(AABB(Vector(-4,0,0),Vector(4,8,8)).Transform(transformMat)) then
|
|
LoadModel(modelpath .. "dungeon/room.wiscene",transformMat )
|
|
-- right
|
|
GenerateDungeon(remaining,pos:Add(Vector(4,0,4):Transform(rotMat)),rotY + 0.5*math.pi)
|
|
-- left
|
|
GenerateDungeon(remaining,pos:Add(Vector(-4,0,4):Transform(rotMat)),rotY - 0.5*math.pi)
|
|
-- straight
|
|
GenerateDungeon(remaining,pos:Add(Vector(0,0,8):Transform(rotMat)),rotY)
|
|
else
|
|
GenerateDungeon(remaining,pos,rotY)
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
-- Place the start piece of the dungeon
|
|
LoadModel(modelpath .. "dungeon/start.wiscene",scalingMat)
|
|
CheckSegmentCanBePlaced(AABB(Vector(-1,0,-2),Vector(1,2,0)))
|
|
-- Call recursive generator function
|
|
GenerateDungeon(complexity,Vector(0,0,0),0)
|
|
MergeExits()
|
|
|
|
|
|
end,
|
|
|
|
}
|
|
|
|
-- Just call this to generate a dungeon of some complexity
|
|
dungeon.Generate(15) |