[src=text]
@name ngb-mazegenerator
@inputs Start Reset MazeSize
@persist [PosX PosY PC MazeSize FloorCount]:number [VisitedFloors Way Walls OuterWalls Floors OuterFloors]:array CurrentFloorEntity:entity
@trigger
################################################################################################################
#Configuration
#
# MazeSize of maze grid
# MazeSize = 10 (uses Input connection value of this chip)
#
# Minimium cells between start and exit cell
ExitDistanceCells = 5
#
# Wall model
Wall = "models/props_phx/construct/metal_plate2x2.mdl"
#
# Wall color
Color1 = vec4(255,0,0,255)
#
# Floor color
Color2 = vec4(255,0,0,255)
#
# Enable debug
Debug = 1
#
# spawning interval in ms (if props dont spawn, try increasing the value)
SpawningInterval = 100
#
# at which rate should the maze generate new cells
MazeGenerationInterval = 1000
################################################################################################################
#Main entry points
################################################################################################################
# Input "Start" changed to 1
if(~Start && Start == 1) {
# start generation process
hint("spawning north walls", 1000)
timer("spawnNorthWallsTimer", 1000)
}
# Input "Reset" changed to 1
if(~Reset && Reset == 1) {
propDeleteAll()
reset()
}
################################################################################################################}
# function definitions
################################################################################################################
function entity getClosestOuterWall(E:entity){
findClearBlackList()
findClearWhiteList()
FindCount = findByModel(Wall)
findClipToEntities(OuterWalls)
ClosestEntity = findClosest(E

os())
ClosestEntity:setColor(vec4(0,0,255,255))
return ClosestEntity
}
# removes a wall between two floors
function void removeWall(){
findClearBlackList()
findClearWhiteList()
FindCount = findByModel(Wall)
findExcludeEntities(OuterWalls)
findClipToEntities(Walls)
Pos = (Way[Way:count(),entity]

os() + CurrentFloorEntity

os()) / 2
ClosestEntity = findClosest(Pos)
ClosestEntity

ropDelete()
if(Debug==1) {
holoCreate(5, ClosestEntity

os())
holoColor(5, vec4(255,0,0,255))
}
}
# get direct neighbour floors of unvisited cells
function array getClosestNeighbourFloors(E:entity){
Dimensions = E:boxSize()
Neighbours = array()
findIncludeEntities(Floors)
FindCount = findByModel(Wall)
findClipToBox(E

os()-vec(Dimensions:x(), 0, 0), E

os()+vec(Dimensions:x(), 0, 0))
Number = findClipFromEntities(VisitedFloors)
for(Index = 1, Number){
EI = findResult(Index)
EI:setColor(vec4(0,255,0,255))
Neighbours

ushEntity(EI)
}
FindCount = findByModel(Wall)
findClipToBox(E

os()-vec(0,Dimensions:y(), 0), E

os()+vec(0,Dimensions:y(), 0))
Number = findClipFromEntities(VisitedFloors)
for(Index = 1, Number){
EI = findResult(Index)
EI:setColor(vec4(0,255,0,255))
Neighbours

ushEntity(EI)
}
# draw holos for debuging
if(Debug==1){
holoCreate(1, E

os()-vec(Dimensions:x(),0,0))
holoCreate(2, E

os()+vec(Dimensions:x(),0,0))
holoCreate(3, E

os()-vec(0,Dimensions:y(),0))
holoCreate(4, E

os()+vec(0,Dimensions:y(),0))
}
return Neighbours
}
# trace back until a unvisited neighbour is found
function void backtrace(){
CurrentFloorEntity = Way

opEntity()
Neighbours = getClosestNeighbourFloors(CurrentFloorEntity)
NeighbourCount = Neighbours:count()
if(NeighbourCount>0){
hint("found unvisited neighbour - resuming from here", 500)
timer("generateMazeTimer", MazeGenerationInterval)
}
else{
if(Way:count()>0){
timer("backtraceTimer", 100)
}
else{
hint("reached start - no more unvisted cells available", 1000)
hint("maze generation finished", 1000)
}
}
}
# main maze generation
function void generateMaze() {
# mark cell as visited
VisitedFloors

ushEntity(CurrentFloorEntity)
# push last visited cell to stack
Way

ushEntity(CurrentFloorEntity)
# get unvisited neighbours
Neighbours = getClosestNeighbourFloors(CurrentFloorEntity)
NeighbourCount = Neighbours:count()
# if it has neighbours...
if(NeighbourCount>0){
# ...select random neighbour cell
RandomNeighbourIndex = randint(NeighbourCount)
RandomNeighbour = Neighbours[RandomNeighbourIndex, entity]
RandomNeighbour:setColor(vec4(0,0,255,255))
#remember for next iteration
CurrentFloorEntity = RandomNeighbour
# remove Wall between cells
removeWall()
# start next iteration timer
timer("generateMazeTimer", 300)
}
else{
# ... no direct neighbour available?
# go back to last visited cell and trace back until a unvisited cell can be found again
CurrentFloorEntity = Way

opEntity()
hint("reached end - starting backtrace", 500)
timer("backtraceTimer", 100)
}
}
# mazeGenerator init function
function void mazeGenerator() {
# Select random start floor in the outer border
FloorCount = OuterFloors:count()
StartFloorIndex = randint(FloorCount)
StartFloorEntity = OuterFloors[StartFloorIndex, entity]
StartFloorEntity:setColor(vec4(0,0,255,255))
CurrentFloorEntity = StartFloorEntity
# Select random exit floor in the outer border
ExitFloorIndex = StartFloorIndex
ExitFloorEntity = StartFloorEntity
while(ExitFloorIndex==StartFloorIndex || StartFloorEntity

os():distance(ExitFloorEntity

os()) < (ExitDistanceCells*StartFloorEntity:boxSize():x())) {
ExitFloorIndex = randint(FloorCount)
ExitFloorEntity = OuterFloors[ExitFloorIndex, entity]
}
ExitFloorEntity:setColor(vec4(0,0,255,255))
# determine and remove outer walls
hint("creating random start and exit", 1000)
StartWall = getClosestOuterWall(StartFloorEntity)
StartWall

ropDelete()
ExitWall = getClosestOuterWall(ExitFloorEntity)
ExitWall

ropDelete()
# Start maze generator
timer("generateMazeTimer", MazeGenerationInterval)
}
# grid generation
################################################################################################################
function void spawnNorthWalls(){
EN = propSpawn(Wall, entity()

os() + vec(0, 0, 50) + vec((PosY-1) * 95, (PosX ) * 95, 0), ang(90,0,0), 1)
if(PosY == 0 || PosY == MazeSize){
EN:setColor(Color1)
OuterWalls

ushEntity(EN)
}
PosY = PosY + 1
if(PosY>MazeSize) {
PosX = PosX + 1
PosY = 0
}
entity():weld(EN)
Walls

ushEntity(EN)
timer("spawnNorthWallsTimer", SpawningInterval)
}
function void spawnWestWalls(){
EW = propSpawn(Wall, entity()

os() + vec(50, 50, 50) + vec((PosY-1) * 95, (PosX - 1) * 95, 0), ang(90,90,0), 1)
if(PosX == 0 || PosX == MazeSize){
EW:setColor(Color1)
OuterWalls

ushEntity(EW)
}
PosY = PosY + 1
if(PosY>(MazeSize - 1)) {
PosX = PosX + 1
PosY = 0
}
entity():weld(EW)
Walls

ushEntity(EW)
timer("spawnWestWallsTimer", SpawningInterval)
}
function void spawnFloor(){
EF = propSpawn(Wall, entity()

os() + vec(50, 0, 0) + vec((PosY-1) * 95, (PosX ) * 95, 0), ang(0,0,0), 1)
if(PosY == 0 || PosY == (MazeSize - 1) || PosX==0 || PosX==(MazeSize - 1)){
EF:setColor(Color2)
OuterFloors

ushEntity(EF)
}
PosY = PosY + 1
if(PosY>(MazeSize - 1)) {
PosX = PosX + 1
PosY = 0
}
Floors

ushEntity(EF)
entity():weld(EF)
timer("spawnFloorTimer", SpawningInterval)
}
################################################################################################################
#Timer listeners
################################################################################################################
if(clk("spawnNorthWallsTimer")) {
PC = PC + 1
if(PC - MazeSize<= MazeSize * MazeSize){
spawnNorthWalls()
}
else{
# reset position once the row has finished spawning
PosX = 0
PosY = 0
PC = 0
hint("spawning west walls", 1000)
timer("spawnWestWallsTimer", SpawningInterval)
}
}
if(clk("spawnWestWallsTimer")) {
PC = PC + 1
if(PC - MazeSize <= MazeSize * MazeSize){
spawnWestWalls()
}
else{
# reset position once the row has finished spawning
PosX = 0
PosY = 0
PC = 0
hint("spawning floors", 1000)
timer("spawnFloorTimer", SpawningInterval)
}
}
if(clk("spawnFloorTimer")) {
PC = PC + 1
if(PC - MazeSize <= MazeSize * MazeSize && PosX<MazeSize){
spawnFloor()
}
else{
# start maze generation process once all walls and floors has been spawned
hint("starting maze generation", 1000)
mazeGenerator()
}
}
if(clk("generateMazeTimer")) {
generateMaze()
}
if(clk("backtraceTimer")){
backtrace()
}
################################################################################################################
[/src]