Filling gaps (like starvants plugin) but live in game

Good news! Last night we got the last script posted here working and we also added a couple more features. It now handles both halves of each wall (inner / outer or in other words left / right) differently so that when coloring them they don’t glitch the graphics where they overlap. In addition, we discovered that when the smallest angle between the walls is less than 90 degrees, the walls can’t be brought together to form a clean point. Doing so makes the wall’s thickness stick out the other end and form an hour glass looking shape around the poll. So, one wall is chosen to form the end and the other wall stops inside of that wall. This code also handles multiple-wall intersections by resizing the left half with the right half of the wall on its left side, and the right half with the left half of the wall on its right side (forward being toward the poll).

If anyone runs across a similar issue at some point, here is the code. Sorry if the math variables are not well named, there so many related in various special ways that it became difficult to find good names for them.

local WALL_HEIGHT = 12
local WALL_OFFSET = Vector3.new(0, WALL_HEIGHT, 0)
local UP = Vector3.new(0, 1, 0)
local WALL_HALF_THICKNESS = 0.2

local abs = math.abs
local function absVector3(v3)
	return Vector3.new(
		abs(v3.X),
		abs(v3.Y),
		abs(v3.Z)
	)
end

local function setSize(wall, half, length)
	local delta = length - wall.length[half]
	local dir = wall.dir * delta
	local part = wall[half]
	local offset = part.CFrame:VectorToObjectSpace(dir)
	part.Size = part.Size + math.sign(delta) * absVector3(offset)
	part.CFrame = part.CFrame + dir / 2
	wall.length[half] = length
end

local function adjust(wall, other)
	local angle = math.acos(wall.rightDir:Dot(-other.rightDir))
	local wallLength, otherLength
	if math.abs(angle - math.pi/2) < 0.001 then
		if wall.dir:Dot(other.rightDir - wall.rightDir) > 0 then
			wallLength = WALL_HALF_THICKNESS
			otherLength = 0
		else
			wallLength = 0
			otherLength = -WALL_HALF_THICKNESS
		end
	elseif angle > math.pi/2 then
		local otherAngle = math.acos(wall.dir:Dot(other.dir))
		local sign = -math.sign(wall.rightDir:Dot(other.dir))
		wallLength = sign * WALL_HALF_THICKNESS / math.tan(otherAngle)
		otherLength = sign * math.sqrt(wallLength^2 + WALL_HALF_THICKNESS^2)
	else
		wallLength = math.tan(angle / 2) * WALL_HALF_THICKNESS
		otherLength = wallLength
	end
	setSize(wall, 'left', wallLength)
	setSize(other, 'right', otherLength)
end

local POLL_KEY = '%d %d %d'
local function getWalls(polls, pos)
	local key = POLL_KEY:format(pos.X, pos.Y, pos.Z)
	local walls = polls[key]
	if not walls then
		walls = {}
		polls[key] = walls
	end
	return walls
end

local function addToPoll(polls, poll, otherPoll, model)
	local walls = getWalls(polls, poll)
	local dir = (poll - otherPoll).Unit
	local rightDir = dir:Cross(UP)
	
	local right, left = model.Wall1, model.Wall2
	local toRight = right.Position - left.Position
	if toRight:Dot(rightDir) < 0 then
		right, left = left, right
	end

	local wall = {
		length = {
			left = 0;
			right = 0;
		};
		model = model;
		right = right;
		left = left;
		dir = dir;
		rightDir = rightDir;
	}
	
	if not next(walls) then
		walls[wall] = true
		return
	end

	local leftIsFound
	local leftDot
	local leftWall
	
	local rightIsFound
	local rightDot
	local rightWall
	for otherWall in next, walls do
		local dot = dir:Dot(otherWall.dir)
		local isRight = rightDir:Dot(otherWall.dir) < 0
		if isRight then
			if not rightIsFound or not rightDot or dot > rightDot then
				rightIsFound = true
				rightDot = dot
				rightWall = otherWall
			end
			if not leftIsFound and (not leftDot or dot < leftDot) then
				leftDot = dot
				leftWall = otherWall
			end
		else
			if not rightIsFound and (not rightDot or dot < rightDot) then
				rightDot = dot
				rightWall = otherWall
			end
			if not leftIsFound or not leftDot or dot > leftDot then
				leftIsFound = true
				leftDot = dot
				leftWall = otherWall
			end
		end
	end

	adjust(wall, leftWall)
	adjust(rightWall, wall)
	walls[wall] = true
end

local function placeWall(polls)
	local mouse = game.Players.LocalPlayer:GetMouse()

	mouse.Button1Down:Wait()
	local pollA = snapToGrid(mouse.Hit.p)

	mouse.Button1Down:Wait()
	local pollB = snapToGrid(mouse.Hit.p)

	local model = wallModel:Clone()
	model:SetPrimaryPartCFrame(CFrame.fromMatrix(
		(pollA + pollB) / 2 + WALL_OFFSET,
		(pollA - pollB).Unit,
		UP
	))

	addToPoll(polls, pollA, pollB, model)
	addToPoll(polls, pollB, pollA, model)
	model.Parent = workspace
end
11 Likes