Loop through array

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Automatic A* generation

  2. What is the issue? My code work! but… I want to be able to rotate my plot, Thats easy but the hard part is I use a positional index to loop through all part with rotated grid

  3. What solutions have you tried so far? Theres prob no resources i can use for this as Even Theme park tycoon have non Rotated grid

After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!

ps Ill open source soon so don’t copy it until i open source it

local module = require(script.GetPart)

workspace.Base.ChildAdded:Connect(function()
	workspace.Model:ClearAllChildren()
	local positiontable = {}
	local waypointable = {}
	local tilesize = 8
	local startick = tick()
	local stuff = workspace.Base:GetChildren()
	for _,v in pairs(stuff) do
		if v:IsA("BasePart") then
			if not v:GetAttribute("Automatic") == false then
				v:SetAttribute("Waypoint",false)
				for _,objv in pairs(v:GetChildren()) do
					if objv:IsA("ObjectValue") then
						objv:Destroy()
						--v.Color = Color3.new(1,1,1)
					end
				end
			end
			if positiontable[tostring(v.Position.X)] == nil then
				positiontable[tostring(v.Position.X)] = {}
				positiontable[tostring(v.Position.X)][tostring(v.Position.Z)] = v
			else
				positiontable[tostring(v.Position.X)][tostring(v.Position.Z)] = v
			end
		end
	end
	for x,xobjects in pairs(positiontable) do
		for z,zobjects in pairs(xobjects) do
			if zobjects:GetAttribute("Automatic") == false then
				table.insert(waypointable,zobjects)
				continue
			end
			local xplus = tostring(tonumber(x)+tilesize)
			local xneg = tostring(tonumber(x)-tilesize)
			local zplus = tostring(tonumber(z)+tilesize)
			local zneg = tostring(tonumber(z)-tilesize)
			if positiontable[xplus] ~= nil then
				if positiontable[xplus][z] ~= nil then
					if positiontable[xneg] ~= nil then
						if positiontable[xneg][z] ~= nil then
							if positiontable[x][zplus] ~= nil then
								if positiontable[x][zneg] ~= nil then
									--zobjects.Color = Color3.new(0,0,0)
									zobjects:SetAttribute("Waypoint",true)
									table.insert(waypointable,zobjects)
									continue
								end
							end
						end
					end
				end
			end
			
			if positiontable[xplus] ~= nil then
				if positiontable[xplus][z] ~= nil then
					if positiontable[xneg] ~= nil then
						if positiontable[xneg][z] == nil then
							--zobjects.Color = Color3.new(0,0,0)
							zobjects:SetAttribute("Waypoint",true)
							table.insert(waypointable,zobjects)
							continue
						end
					else
						--zobjects.Color = Color3.new(0,0,0)
						zobjects:SetAttribute("Waypoint",true)
						table.insert(waypointable,zobjects)
						continue
					end
				end
			end

			if positiontable[x][zplus] ~= nil then
				if positiontable[x][zneg] == nil then
					--zobjects.Color = Color3.new(0,0,0)
					zobjects:SetAttribute("Waypoint",true)
					table.insert(waypointable,zobjects)
					continue
				end
			end

			if positiontable[xneg] ~= nil then
				if positiontable[xneg][z] ~= nil then
					if positiontable[xplus] ~= nil then
						if positiontable[xplus][z] == nil then
							--zobjects.Color = Color3.new(0,0,0)
							zobjects:SetAttribute("Waypoint",true)
							table.insert(waypointable,zobjects)
							continue
						end
					else
						--zobjects.Color = Color3.new(0,0,0)
						zobjects:SetAttribute("Waypoint",true)
						table.insert(waypointable,zobjects)
						continue
					end
				end
			end

			if positiontable[x][zneg] ~= nil then
				if positiontable[x][zplus] == nil then
					--zobjects.Color = Color3.new(0,0,0)
					zobjects:SetAttribute("Waypoint",true)
					table.insert(waypointable,zobjects)
					continue
				end
			end
			--zobjects.Color = Color3.new(1,1,1)
		end
	end
	--print(tick()-startick)
	--end of basic waypoint generation
	--print(waypointable)
	for _,v in pairs(waypointable) do
		--wait(1)
		--v.Color = Color3.new(1,0,0)
		--if not v:GetAttributes("Automatic") == false then
			module.positiveX(positiontable,v,tilesize)
			module.negitiveX(positiontable,v,tilesize)
			module.positiveZ(positiontable,v,tilesize)
			module.negitiveZ(positiontable,v,tilesize)
		--end
		--wait(1)
		--workspace.Model:ClearAllChildren()
	end
	wait()
	for _,v in pairs(waypointable) do
		if v:IsA("BasePart") then
			if v:GetAttribute("Waypoint") == true then
				--v.Color = Color3.new(0,0,0)
				v.Name = "Node"
				--v.Parent = workspace.Nodes
			else
			--	v.Color = Color3.new(1,1,1)
			end


		end
	end
	--print(tick()-startick)
	
end)
local function bruh()
	--wait(4)
	
end
local module = {}
local function makebeam(startpos,endpos,dist)
	local distance = (startpos-endpos).Magnitude
	local beam = Instance.new("Part", workspace.Model)
	beam.Size = Vector3.new(0.3, 0.3, distance)
	beam.Material = Enum.Material.Neon

	beam.Transparency = 0.25

	beam.Anchored = true

	beam.CanCollide = false
	beam.CFrame = CFrame.new(startpos, endpos) * CFrame.new(0,0, -distance/2)
end


function module.positiveX(positiontable,v,tilesize)
	--print("started")
	local start = v.Position.X
	local finded = false
	while finded == false do
		start = start + tilesize
		--print(start)
		if positiontable[tostring(start)] ~= nil then
			if positiontable[tostring(start)][tostring(v.Position.Z)] == nil then
				--print(tostring(start).."x"..v.Position.Z.."z".."Is nil")
				finded = true
			elseif positiontable[tostring(start)][tostring(v.Position.Z)] ~= nil then
				--positiontable[tostring(start)][tostring(v.Position.Z)].Color = Color3.new(0,1,0)
				--wait(1)
				if positiontable[tostring(start)][tostring(v.Position.Z)]:GetAttribute("Waypoint") == true then
					local existed = false
					--for _,objvalue in pairs(positiontable[tostring(start)][tostring(v.Position.Z)]:GetChildren()) do
						--if objvalue:IsA("ObjectValue") then
							--if objvalue.Value == v then
								--existed = true
								--finded = true
								--return
							--end
						--end
					--end
					--if not existed then
						local value  = Instance.new("ObjectValue")
						value.Value = positiontable[tostring(start)][tostring(v.Position.Z)]
						value.Parent = v
						value.Name = "Neighbor"
						makebeam(v.Position,positiontable[tostring(start)][tostring(v.Position.Z)].Position)
							
					--end
					finded = true
				else
					--print(positiontable[tostring(start)][tostring(v.Position.Z)].Color)
				end
			end
		else
			finded = true
		end
	end
end
function module.negitiveX(positiontable,v,tilesize)
	--print("started")
	local start = v.Position.X
	local finded = false
	while finded == false do
		start = start - tilesize
		if positiontable[tostring(start)] ~= nil then
			if positiontable[tostring(start)][tostring(v.Position.Z)] == nil then
				--print(tostring(start).."x"..v.Position.Z.."z".."Is nil")
				finded = true
			elseif positiontable[tostring(start)][tostring(v.Position.Z)] ~= nil then
				--wait(1)
			    --positiontable[tostring(start)][tostring(v.Position.Z)].Color = Color3.new(0,1,0)
				if positiontable[tostring(start)][tostring(v.Position.Z)]:GetAttribute("Waypoint") == true then
					local existed = false
					--for _,objvalue in pairs(positiontable[tostring(start)][tostring(v.Position.Z)]:GetChildren()) do
						--if objvalue:IsA("ObjectValue") then
							--if objvalue.Value == v then
								--existed = true
								--finded = true
								--return
							--end
						--end
					--end
					--if not existed then
						local value  = Instance.new("ObjectValue")
						value.Value = positiontable[tostring(start)][tostring(v.Position.Z)]
						value.Parent = v
						value.Name = "Neighbor"
						makebeam(v.Position,positiontable[tostring(start)][tostring(v.Position.Z)].Position)

					--end
					finded = true
				else
					--print(positiontable[tostring(start)][tostring(v.Position.Z)].Color)
				end
			end
		else
			finded = true
		end
	end
end
function module.positiveZ(positiontable,v,tilesize)
	--print("started")
	local start = v.Position.Z
	local finded = false
	while finded == false do
		start = start + tilesize
		if positiontable[tostring(v.Position.X)] ~= nil then
			if positiontable[tostring(v.Position.X)][tostring(start)] == nil then
				--print(tostring(start).."x"..v.Position.Z.."z".."Is nil")
				finded = true
			elseif positiontable[tostring(v.Position.X)][tostring(start)] ~= nil then
				--wait(1)
				--positiontable[tostring(v.Position.X)][tostring(start)].Color = Color3.new(0,1,0)
				if positiontable[tostring(v.Position.X)][tostring(start)]:GetAttribute("Waypoint") == true then
					local existed = false
					--for _,objvalue in pairs(positiontable[tostring(v.Position.X)][tostring(start)]:GetChildren()) do
						--if objvalue:IsA("ObjectValue") then
							--if objvalue.Value == v then
								--existed = true
								--finded = true
								--return
							--end
						--end
					--end
					--if not existed then
						local value  = Instance.new("ObjectValue")
						value.Value = positiontable[tostring(v.Position.x)][tostring(start)]
						value.Parent = v
						value.Name = "Neighbor"
						makebeam(v.Position,positiontable[tostring(v.Position.x)][tostring(start)].Position)
					--end
					finded = true
				else
					--print(positiontable[tostring(start)][tostring(v.Position.Z)].Color)
				end
			end
		else
			--print("what")
		end
	end
end
function module.negitiveZ(positiontable,v,tilesize)
	--print("started")
	local start = v.Position.Z
	local finded = false
	while finded == false do
		start = start - tilesize
		if positiontable[tostring(v.Position.X)] ~= nil then
			if positiontable[tostring(v.Position.X)][tostring(start)] == nil then
				--print(tostring(start).."x"..v.Position.Z.."z".."Is nil")
				finded = true
			elseif positiontable[tostring(v.Position.X)][tostring(start)] ~= nil then
				--wait(1)
				--positiontable[tostring(v.Position.X)][tostring(start)].Color = Color3.new(0,1,0)
				if positiontable[tostring(v.Position.X)][tostring(start)]:GetAttribute("Waypoint") == true then
					local existed = false
					--for _,objvalue in pairs(positiontable[tostring(v.Position.X)][tostring(start)]:GetChildren()) do
						--if objvalue:IsA("ObjectValue") then
							--if objvalue.Value == v then
								--existed = true
								--finded = true
								--return
							--end
						--end
					--end
					--if not existed then
						local value  = Instance.new("ObjectValue")
						value.Value = positiontable[tostring(v.Position.x)][tostring(start)]
						value.Parent = v
						value.Name = "Neighbor"
						makebeam(v.Position,positiontable[tostring(v.Position.x)][tostring(start)].Position)
					--end
					finded = true
				else
					--print(positiontable[tostring(start)][tostring(v.Position.Z)].Color)
				end
			end
		else
			--print("what")
		end
	end
end
return module

local contextactionservice = game.ContextActionService

--Stuff
local currentindex = 1
local gridsize=8
local objects = game.ReplicatedStorage.Part:GetChildren()
local state = 0
table.sort(objects,function(a,b)
	return a.Id.Value < b.Id.Value
end)


local currentobject = nil
local function grid(x,y,z)
	local x2 = (math.floor(x/gridsize)*gridsize)+gridsize/2
	local z2 = (math.floor(z/gridsize)*gridsize)+gridsize/2
	return Vector3.new(x2,y,z2)
end
local mouse = game.Players.LocalPlayer:GetMouse()
spawn(function()
	while game["Run Service"].RenderStepped:Wait() do
		if currentobject ~= nil then
			local mousehit = mouse.Hit
			local mouseHitRelative = workspace.Plot.CFrame:ToObjectSpace(mousehit)
			local mouseHitRelativeSnapped = CFrame.new(grid(mouseHitRelative.p.X,0.5,mouseHitRelative.Position.Z))
			local mouseHitWorldSnapped = workspace.Plot.CFrame * mouseHitRelativeSnapped
			
			
			currentobject.CFrame = mouseHitWorldSnapped
		end
	end
end)

local function handleTab(actionName, inputState, inputObject)
	if inputState == Enum.UserInputState.Begin then
		print("Handled")
		state = state + 1
		if state == 1 then
			currentobject = objects[currentindex]:Clone() 
			currentobject.Parent = workspace
		elseif state == 2 then
			game.ReplicatedStorage.RemoteEvent:FireServer(currentobject.CFrame,currentindex)
			state = 0
			if currentobject ~= nil then
				currentobject:Destroy()
			end
		end
	end
end
local function handleE(actionName, inputState, inputObject)
	if inputState == Enum.UserInputState.Begin then
		currentindex = math.clamp(currentindex+1,1,#objects)
		if currentobject ~= nil then
			currentobject:Destroy()
			currentobject = objects[currentindex]:Clone() 
			currentobject.Parent = workspace
		end
	end
end
local function handleQ(actionName, inputState, inputObject)
	if inputState == Enum.UserInputState.Begin then
		currentindex = math.clamp(currentindex-1,1,#objects)
		if currentobject ~= nil then
			currentobject:Destroy()
			currentobject = objects[currentindex]:Clone() 
			currentobject.Parent = workspace
		end
	end
end
contextactionservice:BindAction("Place",handleTab,true,Enum.KeyCode.C)
contextactionservice:BindAction("E",handleE,true,Enum.KeyCode.E)
contextactionservice:BindAction("Q",handleQ,true,Enum.KeyCode.Q)

To rotate it, why not just use CFrames to change the origin of the grid?

local function grid(x,y,z)
	local x2 = (math.floor(x/gridsize)*gridsize)+gridsize/2
	local z2 = (math.floor(z/gridsize)*gridsize)+gridsize/2
	return workspace.Plot.CFrame*Vector3.new(x2,y,z2)
end

The rotation will be relative to plot CFrame.

Anyhow, that’s a lot of scripts I would recommend modularizing it.

That is not the problem, The problem is the first two code, Both cant run if the plots are rotated as they assumed that there are 4 cardinal direction being x+1 x-1 z+1 z-1 which work unless the plot are rotated which meant the 4 cardinal direction will also change which may also cause decimal Imprecision which mean my dictionary as seen in the first code will prob break.

If it only works when the plot is unrotated, then transform the rotated coordinates into unrotated coordinates using :VectorToObjectSpace()

Actually nvm, not exactly :VectorToObjectSpace it’s :PointToObjectSpace()

=workspace.Part.CFrame:Inverse()*(workspace.Part.Attachment1.WorldPosition)

Example is an attachment grid:

local part = script.Parent

for x=1, 100 do
	for y =1, 100 do
		local attachment =Instance.new("Attachment")
		attachment.Position = Vector3.new(x,0,-y)
		attachment.Parent =part
	end 
end
local attachmentCF = part.CFrame *attachment.CFrame

You will get the attachments relative position which is 4, 0, -4 with some floating point errors nothing a bit of rounding should fix.

usually it’s recommended to keep your indexes as 1,2,3,4,5 as it’s simple so you can just do the cardinal directions trick as you mentioned.