Reviewing Generation Module Script

Code works perfectly (imo) just wondering if theres any parts i can optimize / make better

modules decently long, probably better to look through the roblox place
TestPlace.rbxl (52.0 KB)

local GenerationValues = require(script.GenValues)
local FloorInfo= require(script.FloorInfo)
local Useful = require(script.Useful)
local Randomizer = Random.new()

local _FloorsFolder = workspace.Floors
local FloorAssets = game:GetService("ServerStorage").FloorAssets
local RunService = game:GetService('RunService')
local FloorGenTime = .01

-- Math
local random = math.random
local rad = math.rad
local randomNew = Random.new

local Floors = {}

local function RNGd(PathValues, CurrentWeight : number, RngTable)
	local CanRoll = {}
	local Rng = Random.new()
	local Counter = 0

	for Path, PathInfo in pairs(PathValues) do
		if PathInfo.Weight <= CurrentWeight then
			local ChanceMult = {}
			local Chance = PathInfo.Chance
			local SpareWeight = CurrentWeight - PathInfo.Weight
			local Rolls = RngTable.RollsSinceLastRolled[Path]
			local MultipleRolls = RngTable.MultipleRolls[Path]
			
			local BaseMullipier = 1

			if SpareWeight > 0 then table.insert(ChanceMult, math.clamp(SpareWeight/55,0,2)) end
			if Rolls > PathInfo.Pity then table.insert(ChanceMult, Rolls/5) end 
			if MultipleRolls > 3 then table.insert(ChanceMult, -(MultipleRolls/5)) end 
			for i, v in ipairs(ChanceMult) do BaseMullipier += v end
			BaseMullipier = math.clamp(BaseMullipier, .1, 4)
			
			local ModifiedChance = math.clamp(Chance * BaseMullipier, 0, 100)


			CanRoll[Path] = ModifiedChance
		end
	end
	for _, Chance in pairs(CanRoll) do
		Counter += Chance
	end

	local Chosen = Rng:NextNumber(0, Counter)

	for Path, Chance in pairs(CanRoll) do
		Counter -= Chance
		if Chosen > Counter then
			return Path
		end
	end
end

local function PathPlacementCheck(AttachTo : Attachment, AttachFrom : Attachment, AttachmentModel : Model, BoundingBoxPart : Part, Olp : OverlapParams)
	for i = 1, 5 do
		local BaseOlp = OverlapParams.new()
		BaseOlp.FilterType = Enum.RaycastFilterType.Include
		BaseOlp.FilterDescendantsInstances = {workspace.Floors}
		
		local PlacementCFrame = AttachTo.WorldCFrame * CFrame.fromOrientation(0, math.rad(90 * (i-1)), 0)
		AttachmentModel:PivotTo(PlacementCFrame)

		local Check1 = (AttachTo.WorldPosition - AttachFrom.WorldPosition).Magnitude < .01
		local OverlappingParts = workspace:GetPartsInPart(BoundingBoxPart, if not Olp then BaseOlp else Olp)
		local Check2 = #OverlappingParts == 0
		

		if Check1 and Check2 then
			return true
		end
	end
	return false
end

local function WallPlacementCheck(WallAttachment : Attachment, AttachFrom : Attachment, ChosenWall : string, WallPathValues)
	if ChosenWall == "openwall" then
		WallAttachment.Parent.Parent = nil
		return true
	elseif WallPathValues[ChosenWall].Instance then
		local Wall = WallPathValues[ChosenWall].Instance:Clone()
		Wall:PivotTo(WallAttachment.Parent.CFrame)
		Wall.Parent = WallAttachment.Parent.Parent
		WallAttachment.Parent.Parent = nil
		return true
	end
end

local function CheckForPathToPathConnection(Floor, TempAttachmentList, AllAttachments) -- Checks If any attachment on the path connections with another paths attachment that is unattached
	for Attachment2, _ in pairs(Floor.ConnectableAttachments) do
		for _, TempAttachment2 in ipairs(TempAttachmentList) do
			if (Attachment2.WorldPosition - TempAttachment2.WorldPosition).Magnitude < .01 then
				Floor.ConnectableAttachments[Attachment2] = nil
				table.remove(TempAttachmentList, table.find(TempAttachmentList, TempAttachment2))
				AllAttachments[Attachment2] = nil
			end
		end
	end
end

local function CheckForPathToWallConnection(Floor, TempAttachmentList, FloorWeight, WallPathValues) -- Checks If any attachment on the path connects to a wall, will roll rng to try and change the wall
	local WallConnectionRngTable = Floor.RollTables.WallConnectionRolls
	for _, TempAttachment2 in ipairs(TempAttachmentList) do
		for _, WallAttachment in ipairs(Floor.ConnectableWallAttachments) do
			if (WallAttachment.WorldPosition - TempAttachment2.WorldPosition).Magnitude < 0.01 then
				table.remove(TempAttachmentList, table.find(TempAttachmentList, TempAttachment2))
				table.remove(Floor.ConnectableWallAttachments, table.find(Floor.ConnectableWallAttachments, WallAttachment))

				local ChosenWall = RNGd(WallPathValues, FloorWeight, WallConnectionRngTable)
				local Result2 = WallPlacementCheck(WallAttachment, TempAttachment2, ChosenWall, WallPathValues)

				if Result2 then
					FloorWeight -= WallPathValues[ChosenWall].Cost
					for Wall, Num in pairs(WallConnectionRngTable.RollsSinceLastRolled) do
						if ChosenWall ~= Wall then
							WallConnectionRngTable.RollsSinceLastRolled[Wall] += 1
							WallConnectionRngTable.MultipleRolls[Wall] = 0
						else
							WallConnectionRngTable.RollsSinceLastRolled[Wall] = 0 
							WallConnectionRngTable.MultipleRolls[Wall] += 1
						end
					end
				end
			end
		end
	end
end

local function CheckForWallToPathConnection(Floor, TempWallAttachmentList, AllAttachments, FloorWeight, WallPathValues) 
	local WallConnectionRngTable = Floor.RollTables.WallConnectionRolls-- Checks if any wall attachment connects to a path, will roll wall (happens pretty rarely)
	for _, TempWallAttachment in ipairs(TempWallAttachmentList) do
		for Attachment2, _ in pairs(AllAttachments) do
			if (TempWallAttachment.WorldPosition - Attachment2.WorldPosition).Magnitude < 0.01 then
				table.remove(TempWallAttachmentList, table.find(TempWallAttachmentList, TempWallAttachment))
				AllAttachments[Attachment2] = nil

				local ChosenWall = RNGd(WallPathValues, FloorWeight, WallConnectionRngTable)
				local Result2 = WallPlacementCheck(TempWallAttachment, Attachment2, ChosenWall, WallPathValues)

				if Result2 then
					FloorWeight -= WallPathValues[ChosenWall].Cost
					for Wall, Num in pairs(WallConnectionRngTable.RollsSinceLastRolled) do
						if ChosenWall ~= Wall then
							WallConnectionRngTable.RollsSinceLastRolled[Wall] += 1
							WallConnectionRngTable.MultipleRolls[Wall] = 0
						else
							WallConnectionRngTable.RollsSinceLastRolled[Wall] = 0 
							WallConnectionRngTable.MultipleRolls[Wall] += 1
						end
					end
				end
			end
		end
	end
end

local GenerationModule = {}
GenerationModule.StartGeneration = function()
	local NonSpecialFloorNums = 0
	
	for Floor, Info in pairs(FloorInfo) do
		local CombinedTable = Useful.CombineTables(Info,  {
			["GenerationStatus"]  = "NotGenerated", 
			["GenerationPercentage"] = 0,
			["CurrentPaths"] = 0,
			["ConnectableAttachments"] = {},
			["ConnectableWallAttachments"] = {},
		})
		if not CombinedTable.SpecialFloor then
			NonSpecialFloorNums += 1 
			
			local FloorFolder = Instance.new("Folder")
			FloorFolder.Name = Floor
			FloorFolder.Parent = _FloorsFolder
		end
		
		Floors[Floor] = CombinedTable
	end
	
	for FloorNum = 1, NonSpecialFloorNums do
		local FloorNameString = "floor" .. FloorNum
		local Floor = Floors[FloorNameString]
		if Floor.GenerationStatus == "NotGenerated" then
			Floor.GenerationStatus = "Generating"
			local PathInfo = GenerationValues[FloorNameString].Paths
			local WallConnectionInfo = GenerationValues[FloorNameString].WallConnections
			
			local PathOverlapParams = OverlapParams.new()
			PathOverlapParams.FilterType = Enum.RaycastFilterType.Include
			PathOverlapParams.FilterDescendantsInstances = {_FloorsFolder}
			
			local TotalPaths = random(Floor.miniumRooms, Floor.maxiumRooms)
			local FirstWeightRoll = Floor.FirstWeightRoll
			local MinWeightRoll = Floor.MinWeightRoll
			local MaxWeightRoll = Floor.MaxWeightRoll
			local CurrentRooms = Floor.CurrentRooms
			local MaxDistance = Floor.MaxDistance
			
			local FloorFolder = _FloorsFolder:FindFirstChild(FloorNameString)
			
			local FloorWeight = 0
			local RollTables = {
				["RoomRolls"] = {
					["RollsSinceLastRolled"] = {},
					["MultipleRolls"] = {}
				},
				["WallConnectionRolls"] ={
					["RollsSinceLastRolled"] = {},
					["MultipleRolls"] = {}
				},
				["WallRolls"] = {
					
				}
			}
			Floor.RollTables = RollTables
			
			local AllAttachments = {}
			local ConnectableAttachments = Floor.ConnectableAttachments
			local ConnectableWallAttachments = Floor.ConnectableWallAttachments
			local StartTime = os.clock()
			
			local RoomRolls = RollTables.RoomRolls
			local WallConnectionRolls = RollTables.WallConnectionRolls
			local WallRolls = RollTables.WallRolls
			
			for Path, _ in pairs(PathInfo) do
				RoomRolls.RollsSinceLastRolled[Path] = 0
				RoomRolls.MultipleRolls[Path] = 0
			end
			
			for ConnectionWall, _ in pairs(WallConnectionInfo) do
				WallConnectionRolls.RollsSinceLastRolled[ConnectionWall] = 0
				WallConnectionRolls.MultipleRolls[ConnectionWall] = 0
			end
 			
			local StartingPath = PathInfo[RNGd(PathInfo, FirstWeightRoll, RoomRolls)].Instance:Clone()
			StartingPath.Parent = FloorFolder
			Floor.CurrentPaths += 1
			StartingPath:PivotTo(CFrame.new(0,100 * (FloorNum-1), 0))
			
			for Num, Attachment in ipairs(StartingPath:GetDescendants()) do
				if Attachment:IsA("Attachment") and Attachment.Name:lower() == "connector" then
					ConnectableAttachments[Attachment] = {["FailedAttaches"] = 0}
				elseif Attachment:IsA("Attachment") and Attachment.Name:lower() == "wallconnector" then
					table.insert(ConnectableWallAttachments, Attachment)
				end
			end
			
			
			while task.wait() do
				local function Pathical(UseWall : boolean)
					local TempAttachmentList = {}	
					local TempWallAttachmentList = {}
					local ChosenPath = RNGd(PathInfo, FloorWeight, RoomRolls)
					local PathModel	= PathInfo[ChosenPath].Instance:Clone()
					
					for Num, Attachment in ipairs(PathModel:GetDescendants()) do
						if Attachment:IsA("Attachment") and Attachment.Name:lower() == "connector" then
							table.insert(TempAttachmentList, Attachment)
						elseif Attachment:IsA("Attachment") and Attachment.Name:lower() == "wallconnector" then
							table.insert(TempWallAttachmentList, Attachment)
						end
					end

					local Cf, Size = PathModel:GetBoundingBox()
					local BoundingBox = Useful.BoundBox(Cf, Size, PathModel)
					
					local function PathAttach(Attachment, AttachmentInfo)
						for TempAttachmentNum, TempAttachment in ipairs(TempAttachmentList) do
							PathModel.PrimaryPart.CFrame = TempAttachment.WorldCFrame
							local Result = PathPlacementCheck(Attachment, TempAttachment, PathModel, BoundingBox, PathOverlapParams)
							
							if Result then
								ConnectableAttachments[Attachment] = nil
								AllAttachments[Attachment] = nil
								table.remove(TempAttachmentList, table.find(TempAttachmentList, TempAttachment))
								CheckForPathToPathConnection(Floor, TempAttachmentList, AllAttachments)
								CheckForPathToWallConnection(Floor, TempAttachmentList, FloorWeight, WallConnectionInfo)
								CheckForWallToPathConnection(Floor, TempWallAttachmentList, AllAttachments, FloorWeight, WallConnectionInfo)
								
								for i, v in ipairs(TempAttachmentList) do
									if (StartingPath.PrimaryPart.Position - v.WorldPosition).Magnitude < MaxDistance then
										ConnectableAttachments[v] = {["FailedAttaches"] = 0}
									end
									
									AllAttachments[v] = false
								end
								
								for i, v in ipairs(TempWallAttachmentList) do
									if (v.WorldPosition - StartingPath.PrimaryPart.Position).Magnitude < MaxDistance then
										table.insert(ConnectableWallAttachments, v)
									end
								end
								
								for Path, Num in pairs(RoomRolls.RollsSinceLastRolled) do
									if ChosenPath ~= Path then
										RoomRolls.RollsSinceLastRolled[Path] += 1
										RoomRolls.MultipleRolls[Path] = 0
									else
										RoomRolls.RollsSinceLastRolled[Path] = 0 
										RoomRolls.MultipleRolls[Path] += 1
									end
								end

								return Result, ChosenPath, PathModel
							elseif not Result then
								AttachmentInfo.FailedAttaches += 1
							end
						end
						return false, ChosenPath, PathModel
					end
					
					local function WallAttach(WallAttachment)
						for TempAttachmentNum, TempAttachment in ipairs(TempAttachmentList) do
							PathModel.PrimaryPart.CFrame = TempAttachment.WorldCFrame
							local Result = PathPlacementCheck(WallAttachment, TempAttachment, PathModel, BoundingBox, PathOverlapParams)
							if Result then
								table.remove(ConnectableWallAttachments, table.find(ConnectableWallAttachments, WallAttachment))
								table.remove(TempAttachmentList, table.find(TempAttachmentList, TempAttachment))
								CheckForPathToPathConnection(Floor, TempAttachmentList, AllAttachments)
								CheckForPathToWallConnection(Floor, TempAttachmentList, FloorWeight, WallConnectionInfo)
								CheckForWallToPathConnection(Floor, TempWallAttachmentList, AllAttachments, FloorWeight, WallConnectionInfo)
								
								for i, v in ipairs(TempAttachmentList) do
									if (StartingPath.PrimaryPart.Position - v.WorldPosition).Magnitude < MaxDistance then
										ConnectableAttachments[v] = {["FailedAttaches"] = 0}
									end

									AllAttachments[v] = false
								end

								for i, v in ipairs(TempWallAttachmentList) do
									if (v.WorldPosition - StartingPath.PrimaryPart.Position).Magnitude < MaxDistance then
										table.insert(ConnectableWallAttachments, v)
									end
								end
								
								for Path, Num in pairs(RoomRolls.RollsSinceLastRolled) do
									if ChosenPath ~= Path then
										RoomRolls.RollsSinceLastRolled[Path] += 1
										RoomRolls.MultipleRolls[Path] = 0
									else
										RoomRolls.RollsSinceLastRolled[Path] = 0 
										RoomRolls.MultipleRolls[Path] += 1
									end
								end

								return Result, ChosenPath, PathModel, WallAttachment
							end
						end
						return false, ChosenPath, PathModel, WallAttachment
					end
					
					if UseWall then
						local WallAttachment = nil
						
						for _, wAttachment in ipairs(ConnectableWallAttachments) do
							task.wait()
							local Result, ChosenPath, PathModel, Attachment = WallAttach(wAttachment)
							WallAttachment = Attachment
							if Result then
								return Result, ChosenPath, PathModel, WallAttachment
							end
						end
						return false, ChosenPath, PathModel, WallAttachment 
					elseif UseWall == nil or not UseWall then
						for Attachment, AttachmentInfo in pairs(ConnectableAttachments) do
							local Result, ChosenPath, PathModel = PathAttach(Attachment, AttachmentInfo)
							if Result then
								return Result, ChosenPath, PathModel
							end
						end
						return false, ChosenPath, PathModel
					end
				end
				
				local function RolledRoom(Result, ChosenPath, PathModel, WallAttachment)
					if Result then
						Floor.CurrentPaths += 1
						PathModel.Parent = FloorFolder
						FloorWeight -= PathInfo[ChosenPath].Cost
						
						if WallAttachment then
							table.remove(ConnectableWallAttachments, table.find(ConnectableWallAttachments, WallAttachment))
							WallAttachment.Parent:Destroy()
						end
					elseif not Result and Result ~= nil then
						PathModel:Destroy()
					end
				end
				
				RolledRoom(Pathical(false))
				local RemainingAttachments = 0
				FloorWeight += Randomizer:NextNumber(MinWeightRoll,MaxWeightRoll)

				for Attachment, AttachmentInfo in pairs(Floor.ConnectableAttachments) do
					RemainingAttachments += 1
					if AttachmentInfo.FailedAttaches >= 5 then
						Floor.ConnectableAttachments[Attachment] = nil
					end
				end
				
				print(math.clamp((Floor.CurrentPaths/TotalPaths)*100, 0, 100) .. "% Complete, Current Floor Weight: " .. FloorWeight)
				if RemainingAttachments == 0 then
					warn("No Remaining attachble attachments, attempting to use wall attachments.")
					local Result, ChosenPath, PathModel, WallAttachment = Pathical(true)
					RolledRoom(Result, ChosenPath, PathModel, WallAttachment)
					if not Result and Result ~= nil then
						warn("Unable to attach to any walls, stopping operation.")
						break
					end
				elseif Floor.CurrentPaths >= TotalPaths then
					print("Generation Complete, Generated: " .. Floor.CurrentPaths .. "/" .. TotalPaths .. " Rooms | Took " .. os.clock() - StartTime .. " Seconds")
					break
				end
			end
			
			for i, v in pairs(AllAttachments) do
				i.Parent.Transparency = 0
			end
		end
	end
end

GenerationModule.__index = GenerationModule
return GenerationModule
1 Like

Remove pairs:

for Path, PathInfo in PathValues do

does pairs slow down the script more than compared to not using it?
just wondering because im using quite a few pairs in a newer script version

In my scripts, it about 7% faster to not have it. If you are not sure you can always test it.

1 Like